Compare commits

...

79 Commits

Author SHA1 Message Date
Lunny Xiao 35c5192b25 Add changelog for 1.22.1 (#31517)
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2024-07-04 10:17:07 +08:00
silverwind e2905761c3 Update golang.org/x/image to v0.18.0 (#31542) 2024-07-02 19:42:16 -04:00
rayden84 50ecae7357 remove typo (#31524) 2024-06-29 15:54:15 +00:00
Giteabot dceadd2ebe Fix avatar radius problem on the new issue page (#31506) (#31508)
Backport #31506 by charles7668

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
2024-06-27 23:34:40 +08:00
Giteabot 3cecfa3df4 use correct l10n string (#31487) (#31490)
Backport #31487 by CyberFlameGO

Co-authored-by: CyberFlame <cyberflameu@gmail.com>
2024-06-26 13:45:48 +00:00
silverwind d9e6657879 Upgrade github.com/hashicorp/go-retryablehttp (#31499) 2024-06-26 13:15:42 +00:00
wxiaoguang 151b786837 Fix dropzone JS error when attachment is disabled (#31486)
Fix #31485
2024-06-26 18:09:25 +08:00
Giteabot 58b1ee5ebd Fix overflow menu flickering on mobile (#31484) (#31488)
Backport #31484 by brechtvl

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-26 01:13:04 +08:00
Giteabot 1825c316a6 Fix poor table column width due to breaking words (#31473) (#31477)
Backport #31473 by brechtvl

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
Co-authored-by: silverwind <me@silverwind.io>
2024-06-25 08:00:17 +08:00
Giteabot d1dd3fa49c Fix deprecated Dockerfile ENV format (#31450) (#31452)
Backport #31450 by @silverwind

See
https://docs.docker.com/reference/build-checks/legacy-key-value-format/.
Fixes these warnings seen during the docker build:

```
 4 warnings found (use --debug to expand):
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 5)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 9)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 75)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 76)
 ```

Introduced in: https://github.com/moby/buildkit/pull/4923

Co-authored-by: silverwind <me@silverwind.io>
2024-06-24 00:53:37 +00:00
Giteabot eaeb4d1b96 Fix web notification icon not updated once you read all notifications (#31447) (#31466)
Backport #31447 by kiatt210

Fix #29065
Remove status filtering from GetUIDsAndNotificationCounts sql.

Co-authored-by: kiatt210 <40639725+kiatt210@users.noreply.github.com>
Co-authored-by: kiatt210 <kiatt210@github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-24 00:08:37 +08:00
techknowlogick 688085c15e Switch to upstream of gorilla/feeds (#31400) (#31464)
backport #31400
2024-06-23 16:47:43 +08:00
Giteabot 8d60c7d568 Switch to "Write" tab when edit comment again (#31445) (#31461)
Backport #31445 by wxiaoguang

Fix #19031

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-22 23:47:36 +08:00
Giteabot be302f3025 Support relative paths to videos from Wiki pages (#31061) (#31453)
Backport #31061 by @sergeyvfx

This change fixes cases when a Wiki page refers to a video stored in the
Wiki repository using relative path. It follows the similar case which
has been already implemented for images.

Test plan:
- Create repository and Wiki page
- Clone the Wiki repository
- Add video to it, say `video.mp4`
- Modify the markdown file to refer to the video using `<video
src="video.mp4">`
- Commit the Wiki page
- Observe that the video is properly displayed

Co-authored-by: Sergey Sharybin <sergey.vfx@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-22 00:03:31 +03:00
Giteabot a3529d662f Fix the link for .git-blame-ignore-revs bypass (#31432) (#31442)
Backport #31432 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-21 14:47:11 +08:00
Giteabot a4f977e00b Fix the wrong line number in the diff view page when expanded twice. (#31431) (#31440)
Backport #31431 by @charles7668

close #31149

The problem is caused by the `data-query=` string affecting the parsing
of the `last_left` parameter. When the page is first rendered,
`data-query=` does not exist. However, after clicking expand, the
returned HTML contains this string, causing the line numbers to start
from 1 upon expanding a second time.

before fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/36842bde-cc30-43ba-91b0-c60a50a6dc0f)

after fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/b73d7e40-ec01-48ae-9992-09c19ee477ad)

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
2024-06-20 20:33:51 +00:00
Giteabot b83482b4ba Fix labels and projects menu overflow on issue page (#31435) (#31439)
Backport #31435 by brechtvl

It was correct only on the new issue page.

Resolves #31415

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-20 16:59:49 +00:00
Giteabot 9ecaeda66e [Fix] Account Linking UpdateMigrationsByType (#31428) (#31434)
Backport #31428 by Sumit189

Co-authored-by: Sumit <sumit.18.paul@gmail.com>
2024-06-20 13:53:15 +00:00
Giteabot 7fbcc58062 Fix markdown math brackets render problem (#31420) (#31430)
Backport #31420 by charles7668

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-20 17:45:08 +08:00
Giteabot 05f32114d1 Increase max length of org team names from 30 to 255 characters (#31410) (#31421)
Backport #31410 by tobiasbp

This PR modifies the structs for editing and creating org teams to allow
team names to be up to 255 characters. The previous maximum length was
30 characters.

Co-authored-by: Tobias Balle-Petersen <tobias.petersen@unity3d.com>
2024-06-20 01:27:13 +00:00
Giteabot 5c79c2b431 Fix new issue/pr avatar (#31419) (#31424)
Backport #31419 by @silverwind

The avatar on "New Issue" and "New Pull Request" pages was inconsistent.
Removed the extra margin and the new CSS rules now use common parent
`<form id="#new-issue">` because `.repository.new.issue` is not present
on pull request page.

Before:

<img width="181" alt="Screenshot 2024-06-19 at 13 56 17"
src="https://github.com/go-gitea/gitea/assets/115237/5270d352-db5b-45b3-9d06-4790c17ae9b4">
<img width="213" alt="Screenshot 2024-06-19 at 13 54 02"
src="https://github.com/go-gitea/gitea/assets/115237/012f5607-aef0-4f48-90e3-8d4022480203">


After:

<img width="195" alt="Screenshot 2024-06-19 at 13 54 16"
src="https://github.com/go-gitea/gitea/assets/115237/e7590c66-3b28-4790-9970-33bd567eeb31">
<img width="212" alt="Screenshot 2024-06-19 at 13 54 22"
src="https://github.com/go-gitea/gitea/assets/115237/8e1cfede-614c-4cea-9af2-ada6da7a7361">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-19 21:18:10 +00:00
Giteabot 3813b27862 Reduce air verbosity (#31417) (#31425)
Backport #31417 by @silverwind

Make `air` log less. Uses the option added in
https://github.com/air-verse/air/pull/367.

Co-authored-by: silverwind <me@silverwind.io>
2024-06-20 04:57:28 +08:00
wxiaoguang 042e9fcd81 Fix rendered wiki page link (#31398) (#31407)
Backport #31398

Fix #31395
2024-06-19 11:23:24 +08:00
Giteabot e8e43a7ee4 Fix double border in system status table (#31363) (#31401)
Backport #31363 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-18 06:29:43 +08:00
Lunny Xiao a9d1e4311e Fix bug filtering issues which have no project (#31337) (#31367) 2024-06-17 10:23:15 +00:00
Giteabot ed0fc0ec46 Fix natural sort (#31384) (#31394)
Backport #31384 by wxiaoguang

Fix #31374

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-17 07:41:47 +00:00
wxiaoguang fa307167f9 Fix missing images in editor preview due to wrong links (#31299) (#31393)
Backport #31299

Parse base path and tree path so that media links can be correctly
created with /media/.

Resolves #31294

---------

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-17 15:07:21 +08:00
Giteabot 3f44844244 Allow downloading attachments of draft releases (#31369) (#31380)
Backport #31369 by Zettat123

Fix #31362

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-06-16 20:55:14 +08:00
wxiaoguang 52925e9c7c Fix duplicate sub-path for avatars (#31365) (#31368)
Backport #31365, only backport necessary changes.
2024-06-15 03:44:44 +00:00
wxiaoguang 188e515efc Fix repo graph JS (#31377)
Fix #31376
Regression of #30395
2024-06-14 18:21:40 +02:00
wxiaoguang cdd057c7a3 Backport Iif (#31353) 2024-06-13 14:52:34 +08:00
Giteabot 6d0d4640f6 Fixed incorrect localization explorer.go (#31348) (#31350)
Backport #31348 by @kerwin612

see: https://github.com/go-gitea/gitea/pull/29701/files#r1637325139

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-13 02:16:17 +00:00
Giteabot 6ca70c5bf2 Fix hash render end with colon (#31319) (#31346)
Backport #31319 by @lunny

Fix a hash render problem like `<hash>: xxxxx` which is usually used in
release notes.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-13 01:14:49 +00:00
Giteabot 95dfd945bc Fix line number widths (#31341) (#31343)
Backport #31341 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-13 00:13:35 +00:00
Giteabot 568ff1015b Fix adopt repository has empty object name in database (#31333) (#31335)
Backport #31333 by @lunny

Fix #31330
Fix #31311

A workaround to fix the old database is to update object_format_name to
`sha1` if it's empty or null.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-12 15:59:54 +00:00
Giteabot 4b6ef9265b Fix navbar + menu flashing on page load (#31281) (#31342)
Backport #31281 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-12 15:26:04 +00:00
Giteabot b1ad8ccb73 Reduce memory usage for chunked artifact uploads to MinIO (#31325) (#31338)
Backport #31325 by @bohde

When using the MinIO storage driver for Actions Artifacts, we found that
the chunked artifact required significantly more memory usage to both
upload and merge than the local storage driver. This seems to be related
to hardcoding a value of `-1` for the size to the MinIO client [which
has a warning about memory usage in the respective
docs](https://pkg.go.dev/github.com/minio/minio-go/v7#Client.PutObject).
Specifying the size in both the upload and merge case reduces memory
usage of the MinIO client.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
2024-06-12 16:25:46 +02:00
Giteabot 758f84f33e Fix #31185 try fix lfs download from bitbucket failed (#31201) (#31329)
Backport #31201 by @Zoupers

Fix #31185

Co-authored-by: Zoupers Zou <1171443643@qq.com>
2024-06-12 02:34:37 +03:00
Giteabot 3fcf865a4b Delete legacy cookie before setting new cookie (#31306) (#31317)
Backport #31306 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-11 05:57:51 +00:00
Giteabot c1c11aaf60 Fix line number width in code preview (#31307) (#31316)
Backport #31307 by silverwind

Line numbers were using some hacky CSS `width: 1%` that did nothing to
the code rendering as far as I can tell but broken the inline preview in
markup when line numbers are greater than 2 digits. Also I removed one
duplicate `font-family` rule (it is set below in the `.lines-num,
.lines-code` selector.

Co-authored-by: silverwind <me@silverwind.io>
2024-06-11 13:30:02 +08:00
6543 abc92df701 Add nix flake for dev shell (#30967) (#31310)
Backport #30967
2024-06-11 09:50:13 +08:00
Giteabot 1dc8a66074 Remove sub-path from container registry realm (#31293) (#31300)
Backport #31293 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-10 12:04:49 +08:00
Giteabot bbe98a3254 Fix some URLs whose sub-path is missing (#31289) (#31292)
Backport #31289 by wxiaoguang

Fix #31285

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-07 15:43:47 +00:00
Giteabot 5ca4c6d066 Fix and clean up ConfirmModal (#31283) (#31291)
Backport #31283 by @silverwind

Bug: orange button color was removed in
https://github.com/go-gitea/gitea/pull/30475, replaced with red
Bug: translation text was not html-escaped
Refactor: Replaced as much jQuery as possible, added useful
`createElementFromHTML`
Refactor: Remove colors checks that don't exist on `.link-action`

<img width="381" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/5900bf6a-8a86-4a86-b368-0559cbfea66e">

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
2024-06-07 15:51:54 +02:00
Giteabot 75e0bdcec5 Optimize repo-list layout to enhance visual experience (#31272) (#31276)
Backport #31272 by @kerwin612

before: 

![1717655078227](https://github.com/go-gitea/gitea/assets/3371163/4d564f96-c2f8-46b1-996f-6cc7abb940ef)
***The problem was that the icon and text were not on a horizontal line,
and the horizontal was not centered;***

after: 

![1717655094071](https://github.com/go-gitea/gitea/assets/3371163/b11797f6-05f8-486c-b5fd-df89d0cbdcfd)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-07 04:37:47 +00:00
Giteabot a918757105 fixed the dropdown menu for the top New button to expand to the left (#31273) (#31275)
Backport #31273 by @kerwin612

before: 

![1717660314025](https://github.com/go-gitea/gitea/assets/3371163/17ae7a48-31c5-4c71-b285-f65d9106bf86)

after: 

![1717660674763](https://github.com/go-gitea/gitea/assets/3371163/85f847ac-a044-4695-9004-26e6485288c6)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-07 04:19:00 +02:00
Giteabot c07416b3d0 Fix Activity Page Contributors dropdown (#31264) (#31269)
Backport #31264 by wxiaoguang

Fix #31261

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-06 08:14:00 +00:00
Giteabot 875579cc65 Add replacement module for mholt/archiver (#31267) (#31270)
Backport #31267 by @silverwind

Switch to this fork tag:
https://github.com/anchore/archiver/releases/tag/v3.5.2 which includes
https://github.com/anchore/archiver/commit/82ca88a2eb24d418c30bf960ef071b0bbec04631.

Ref: https://pkg.go.dev/vuln/GO-2024-2698
Ref: https://github.com/advisories/GHSA-rhh4-rh7c-7r5v

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-06 04:04:47 +00:00
Giteabot 83cf348e07 Optimize runner-tags layout to enhance visual experience (#31258) (#31263)
Backport #31258 by @kerwin612


![image](https://github.com/go-gitea/gitea/assets/3371163/b8199005-94f2-45be-8ca9-4fa1b3f221b2)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-06 04:35:36 +08:00
Giteabot 7cb67cfd7f fix: allow actions artifacts storage migration to complete succesfully (#31251) (#31257)
Backport #31251 by @bohde

Change the copy to use `ActionsArtifact.StoragePath` instead of the
`ArtifactPath`. Skip artifacts that are expired, and don't error if the
file to copy does not exist.

---

When trying to migrate actions artifact storage from local to MinIO, we
encountered errors that prevented the process from completing
successfully:

* The migration tries to copy the files using the per-run
`ArtifactPath`, instead of the unique `StoragePath`.
* Artifacts that have been marked expired and had their files deleted
would throw an error
* Artifacts that are pending, but don't have a file uploaded yet will
throw an error.

This PR addresses these cases, and allow the process to complete
successfully.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
2024-06-05 11:25:48 +03:00
Giteabot 1c1c2d36e8 Make blockquote attention recognize more syntaxes (#31240) (#31250)
Backport #31240 by wxiaoguang

Fix #31214

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-04 16:03:01 +00:00
Giteabot 082600a50e Fix admin oauth2 custom URL settings (#31246) (#31247)
Backport #31246 by wxiaoguang

Fix #31244

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-04 15:11:07 +00:00
Giteabot 5136c879c2 Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
Backport #31235 by wxiaoguang

Fix #31230

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-04 13:26:55 +00:00
Thomas Desveaux ca414a7ccf Fix NuGet Package API for $filter with Id equality (#31188) (#31242)
Backport #31188

Fixes issue when running `choco info pkgname` where `pkgname` is also a
substring of another package Id.

Relates to #31168

---

This might fix the issue linked, but I'd like to test it with more choco
commands before closing the issue in case I find other problems if
that's ok.
I'm pretty inexperienced with Go, so feel free to nitpick things.

Not sure I handled
[this](https://github.com/tdesveaux/gitea/blob/70f87e11b5caf1ee441ae71c7eba1831f45897d4/routers/api/packages/nuget/nuget.go#L135-L137)
in the best way, so looking for feedback on if I should fix the
underlying issue (`nil` might be a better default for `Value`?).

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-06-04 14:56:59 +03:00
Giteabot 331c32f9b6 Fix overflow on push notification (#31179) (#31238)
Backport #31179 by @silverwind

Fixes: https://github.com/go-gitea/gitea/issues/30063

<img width="1301" alt="Screenshot 2024-05-30 at 14 43 24"
src="https://github.com/go-gitea/gitea/assets/115237/00443af0-088d-49a5-be9e-8c9adcc2c01d">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 10:15:25 +00:00
Giteabot 298d05df3b Remove .segment from .project-column (#31204) (#31239)
Backport #31204 by @silverwind

Using `.segment` on the project columns is a major abuse of that class,
so remove it and instead set the border-radius directly on it.

Fixes: https://github.com/go-gitea/gitea/issues/31129

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 09:45:50 +00:00
Giteabot 85a8176708 Fix overflow on notifications (#31178) (#31237)
Backport #31178 by @silverwind

Fixes https://github.com/go-gitea/gitea/issues/31170.

<img width="1312" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/627711ed-93ca-4be6-b958-10d673ae9517">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 11:17:00 +02:00
Giteabot 0b5012c6fc Update air package path (#31233) (#31236) 2024-06-04 05:28:21 +00:00
Giteabot 0328f31fdc Fix overflow in issue card (#31203) (#31225)
Backport #31203 by @silverwind

Before:

<img width="373" alt="Screenshot 2024-06-01 at 01 31 26"
src="https://github.com/go-gitea/gitea/assets/115237/82a210f2-c82e-4b7e-ac43-e70e46fa1186">

After:
<img width="376" alt="Screenshot 2024-06-01 at 01 31 32"
src="https://github.com/go-gitea/gitea/assets/115237/82d1b9f7-4fad-47bd-948a-04e1e7e006e6">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-03 02:14:15 +00:00
Giteabot 33fa93a952 Fix agit checkout command line hint & fix ShowMergeInstructions checking (#31219) (#31222)
Backport #31219 by @lunny

Fix #31135 

Before
<img width="527" alt="图片"
src="https://github.com/go-gitea/gitea/assets/81045/60c07b55-3db2-4a9a-98f1-0eb72d149df2">

After
<img width="664" alt="图片"
src="https://github.com/go-gitea/gitea/assets/81045/0670ff5d-9e0b-47ac-aba4-9db9131de3b3">

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-03 09:45:57 +08:00
Giteabot 68e405cf0b Fix the possible migration failure on 286 with postgres 16 (#31209) (#31218)
Backport #31209 by @lunny

Try to fix #31205

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-02 10:19:30 +03:00
Giteabot b6280f4d21 Split sanitizer functions and fine-tune some tests (#31192) (#31200)
Backport #31192 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-31 13:54:14 +00:00
Giteabot 1987c86f3c Fix URL In Gitea Actions Badge Docs (#31191) (#31198)
Backport #31191 by @TheBrokenRail

The example URL given in the documentation leads to a 404.

For instance,
`https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file}?branch={branch}&event={event}`
translates to
`https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions/workflows/build.yml`,
which is a 404.

I had to check the [linked GitHub
docs](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge)
to learn that you have to add `/badge.svg` to the URL.

Example:
https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions/workflows/build.yml/badge.svg

Co-authored-by: TheBrokenRail <17478432+TheBrokenRail@users.noreply.github.com>
2024-05-31 21:08:04 +08:00
Giteabot c6176ee59f Fix branch order (#31174) (#31193)
Backport #31174 by @lunny

Fix #31172

The original order or the default order should not be ignored even if we
have an is_deleted order.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-31 11:34:05 +08:00
Giteabot e8c776c793 Fix push multiple branches error with tests (#31151) (#31153)
Backport #31151 by @lunny

Fix #31140 

The previous logic is wrong when pushing multiple branches. After first
branch updated, it will ignore left other branches sync operations.

As a workaround for the repositories, just push a new commit after the
patch applied will fix the repositories status.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-30 07:04:42 +00:00
wxiaoguang cc64d4d2b2 Ignore FindRecentlyPushedNewBranches err (#31164) (#31171)
Backport #31164
2024-05-30 14:24:14 +08:00
Giteabot f0d55e4819 Swap word order in Comment and Close (#31148) (#31165)
Backport #31148 by @techknowlogick

Reduce accident closing of tickets only to re-open them right away. This
aligns the text on these buttons with what GitHub has.

Commit is authored by @LazyDodo, and was committed to the Blender fork
by @brechtvl

Background details:
https://projects.blender.org/infrastructure/gitea-custom/pulls/7

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-authored-by: Ray Molenkamp <github@lazydodo.com>
2024-05-30 01:07:27 +00:00
Giteabot fb14ca30eb Fix markup preview (#31158) (#31166)
Backport #31158 by @wxiaoguang

Fix #31157

After:


![image](https://github.com/go-gitea/gitea/assets/2114189/4d918cce-cd0d-4601-9c81-4b32df1b0b38)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-30 02:38:30 +02:00
Giteabot a672f066f7 Use vertical layout for multiple code expander buttons (#31122) (#31152)
Backport #31122 by @silverwind

Fixes: https://github.com/go-gitea/gitea/issues/31068

- Now it only does a single call to `GetExpandDirection` per line
instead of multiples.
- Exposed `data-expand-direction` to frontend so it can correctly size
the buttons (it's a pain to do in tables).

<img width="142" alt="Screenshot 2024-05-27 at 20 44 56"
src="https://github.com/go-gitea/gitea/assets/115237/8b0b45a6-8e50-4081-8822-5e0775d8d941">
<img width="142" alt="Screenshot 2024-05-27 at 20 44 51"
src="https://github.com/go-gitea/gitea/assets/115237/b7ba2c57-8f55-4e9f-9606-c96d16b77892">
<img width="132" alt="Screenshot 2024-05-27 at 20 44 46"
src="https://github.com/go-gitea/gitea/assets/115237/0e838fb8-5e8c-4250-9843-a68b88d5418b">
<img width="80" alt="Screenshot 2024-05-27 at 20 44 33"
src="https://github.com/go-gitea/gitea/assets/115237/da6c7f83-c160-4389-8ab2-889d0568cbe8">
<img width="80" alt="Screenshot 2024-05-27 at 20 44 26"
src="https://github.com/go-gitea/gitea/assets/115237/cdb490b2-5040-484a-92e5-46fc5e37c199">
<img width="78" alt="Screenshot 2024-05-27 at 20 44 20"
src="https://github.com/go-gitea/gitea/assets/115237/d2978ab0-764e-41ff-922c-25f8fe749f28">

Would backport as trivial enhancement.

Co-authored-by: silverwind <me@silverwind.io>
2024-05-29 09:06:34 +02:00
Giteabot c6189cfcb9 Add an immutable tarball link to archive download headers for Nix (#31139) (#31145)
Backport #31139 by @Mic92

This allows `nix flake metadata` and nix in general to lock a *branch*
tarball link in a manner that causes it to fetch the correct commit even
if the branch is updated with a newer version.

For further context, Nix flakes are a feature that, among other things,
allows for "inputs" that are "github:someuser/somerepo",
"https://some-tarball-service/some-tarball.tar.gz",
"sourcehut:~meow/nya" or similar. This feature allows our users to fetch
tarballs of git-based inputs to their builds rather than using git to
fetch them, saving significant download time.

There is presently no gitea or forgejo specific fetcher in Nix, and we
don't particularly wish to have one. Ideally (as a developer on a Nix
implementation myself) we could just use the generic tarball fetcher and
not add specific forgejo support, but to do so, we need additional
metadata to know which commit a given *branch* tarball represents, which
is the purpose of the Link header added here.

The result of this patch is that a Nix user can specify
`inputs.something.url =
"https://forgejo-host/some/project/archive/main.tar.gz"` in flake.nix
and get a link to some concrete tarball for the actual commit in the
lock file, then when they run `nix flake update` in the future, they
will get the latest commit in that branch.

Example of it working locally:

» nix flake metadata --refresh
'http://localhost:3000/api/v1/repos/jade/cats/archive/main.tar.gz?dir=configs/nix'
Resolved URL:
http://localhost:3000/api/v1/repos/jade/cats/archive/main.tar.gz?dir=configs/nix
Locked URL:
http://localhost:3000/api/v1/repos/jade/cats/archive/804ede182b6b66469b23ea4d21eece52766b7a06.tar.gz?dir=configs
/nix&narHash=sha256-yP7KkDVfuixZzs0fsqhSETXFC0y8m6nmPLw2GrAMxKQ%3D
Description:   Computers with the nixos
Path:          /nix/store/s856c6yqghyan4v0zy6jj19ksv0q22nx-source
Revision:      804ede182b6b66469b23ea4d21eece52766b7a06
Last modified: 2024-05-02 00:48:32

For details on the header value, see:

https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md

Co-authored-by: Jörg Thalheim <Mic92@users.noreply.github.com>
Co-authored-by: Jade Lovelace <software@lfcode.ca>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-29 01:34:14 +00:00
Giteabot 42b2541cb5 Update demo site location from try.gitea.io -> demo.gitea.com (#31054) (#31146)
Backport #31054 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-29 01:05:07 +00:00
Giteabot 9c93c6249c Remove duplicate ProxyPreserveHost in Apache httpd doc (#31143) (#31147)
Backport #31143 by @HorlogeSkynet

---

(fix up for #31003)

Co-authored-by: Samuel FORESTIER <HorlogeSkynet@users.noreply.github.com>
2024-05-29 08:54:14 +08:00
Giteabot b615a59db8 Improve mobile review ui (#31091) (#31136)
Backport #31091 by @silverwind

Fixes: https://github.com/go-gitea/gitea/issues/31071

Not perfect but much better than before.

Before: Overflows, sticky not working, filename unreadable:

<img width="506" alt="Screenshot 2024-05-27 at 02 02 40"
src="https://github.com/go-gitea/gitea/assets/115237/a06b1edf-dece-4402-98c2-68670fca265f">

After:
<img width="457" alt="Screenshot 2024-05-27 at 01 59 06"
src="https://github.com/go-gitea/gitea/assets/115237/2a282c96-e719-4554-b418-81963ae6269c">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-28 14:10:01 +00:00
Giteabot e6bacf1fed Add missed return after ctx.ServerError (#31130) (#31133)
Backport #31130 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-28 12:47:11 +02:00
Giteabot d6ae2b3c4e Fix API repository object format missed (#31118) (#31132)
Backport #31118 by @lunny

Fix #31117

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-28 09:59:56 +00:00
Giteabot 447b3e2475 Fix DashboardRepoList margin (#31121) (#31128)
Backport #31121 by @silverwind

Fixes: https://github.com/go-gitea/gitea/issues/31115

<img width="476" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/ba508ba9-b02d-47c6-ad9f-495101c81330">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-28 17:12:15 +08:00
Giteabot 7ecb1d63bb Move documents under actions (#31110) (#31116)
Backport #31110 by @lunny

Move secrets and badge under actions

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-27 23:05:49 +08:00
Giteabot b0981f6509 Fix missing memcache import (#31105) (#31109)
Backport #31105 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-27 14:23:47 +00:00
Giteabot 7f706bd171 Update pip related commands for docker (#31106) (#31111)
Backport #31106 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-27 21:54:19 +08:00
158 changed files with 1736 additions and 1000 deletions
+3
View File
@@ -21,3 +21,6 @@ exclude_dir = [
]
exclude_regex = ["_test.go$", "_gen.go$"]
stop_on_error = true
[log]
main_only = true
+2 -2
View File
@@ -3,7 +3,7 @@
<!--
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your issue doesn't already exist.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
5. Please give all relevant information below for bug reports, because
@@ -21,7 +21,7 @@
- [ ] MySQL
- [ ] MSSQL
- [ ] SQLite
- Can you reproduce the bug at https://try.gitea.io:
- Can you reproduce the bug at https://demo.gitea.com:
- [ ] Yes (provide example URL)
- [ ] No
- Log gist:
+2 -2
View File
@@ -37,7 +37,7 @@ body:
label: Can you reproduce the bug on the Gitea demo site?
description: |
If so, please provide a URL in the Description field
URL of Gitea demo: https://try.gitea.io
URL of Gitea demo: https://demo.gitea.com
options:
- "Yes"
- "No"
@@ -74,7 +74,7 @@ body:
attributes:
label: How are you running Gitea?
description: |
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://try.gitea.io or are using some other package
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://demo.gitea.com or are using some other package
Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
If you are using a package or systemd tell us what distribution you are using
validations:
+1 -1
View File
@@ -46,7 +46,7 @@ body:
label: Can you reproduce the bug on the Gitea demo site?
description: |
If so, please provide a URL in the Description field
URL of Gitea demo: https://try.gitea.io
URL of Gitea demo: https://demo.gitea.com
options:
- "Yes"
- "No"
+73
View File
@@ -4,6 +4,79 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.22.1](https://github.com/go-gitea/gitea/releases/tag/1.22.1) - 2024-07-04
* SECURITY
* Add replacement module for `mholt/archiver` (#31267) (#31270)
* API
* Fix missing images in editor preview due to wrong links (#31299) (#31393)
* Fix duplicate sub-path for avatars (#31365) (#31368)
* Reduce memory usage for chunked artifact uploads to MinIO (#31325) (#31338)
* Remove sub-path from container registry realm (#31293) (#31300)
* Fix NuGet Package API for $filter with Id equality (#31188) (#31242)
* Add an immutable tarball link to archive download headers for Nix (#31139) (#31145)
* Add missed return after `ctx.ServerError` (#31130) (#31133)
* BUGFIXES
* Fix avatar radius problem on the new issue page (#31506) (#31508)
* Fix overflow menu flickering on mobile (#31484) (#31488)
* Fix poor table column width due to breaking words (#31473) (#31477)
* Support relative paths to videos from Wiki pages (#31061) (#31453)
* Fix new issue/pr avatar (#31419) (#31424)
* Increase max length of org team names from 30 to 255 characters (#31410) (#31421)
* Fix line number width in code preview (#31307) (#31316)
* Optimize runner-tags layout to enhance visual experience (#31258) (#31263)
* Fix overflow on push notification (#31179) (#31238)
* Fix overflow on notifications (#31178) (#31237)
* Fix overflow in issue card (#31203) (#31225)
* Split sanitizer functions and fine-tune some tests (#31192) (#31200)
* use correct l10n string (#31487) (#31490)
* Fix dropzone JS error when attachment is disabled (#31486)
* Fix web notification icon not updated once you read all notifications (#31447) (#31466)
* Switch to "Write" tab when edit comment again (#31445) (#31461)
* Fix the link for .git-blame-ignore-revs bypass (#31432) (#31442)
* Fix the wrong line number in the diff view page when expanded twice. (#31431) (#31440)
* Fix labels and projects menu overflow on issue page (#31435) (#31439)
* Fix Account Linking UpdateMigrationsByType (#31428) (#31434)
* Fix markdown math brackets render problem (#31420) (#31430)
* Fix rendered wiki page link (#31398) (#31407)
* Fix natural sort (#31384) (#31394)
* Allow downloading attachments of draft releases (#31369) (#31380)
* Fix repo graph JS (#31377)
* Fix incorrect localization `explorer.go` (#31348) (#31350)
* Fix hash render end with colon (#31319) (#31346)
* Fix line number widths (#31341) (#31343)
* Fix navbar `+` menu flashing on page load (#31281) (#31342)
* Fix adopt repository has empty object name in database (#31333) (#31335)
* Delete legacy cookie before setting new cookie (#31306) (#31317)
* Fix some URLs whose sub-path is missing (#31289) (#31292)
* Fix admin oauth2 custom URL settings (#31246) (#31247)
* Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
* Fix agit checkout command line hint & fix ShowMergeInstructions checking (#31219) (#31222)
* Fix the possible migration failure on 286 with postgres 16 (#31209) (#31218)
* Fix branch order (#31174) (#31193)
* Fix markup preview (#31158) (#31166)
* Fix push multiple branches error with tests (#31151) (#31153)
* Fix API repository object format missed (#31118) (#31132)
* Fix missing memcache import (#31105) (#31109)
* Upgrade `github.com/hashicorp/go-retryablehttp` (#31499)
* Fix double border in system status table (#31363) (#31401)
* Fix bug filtering issues which have no project (#31337) (#31367)
* Fix #31185 try fix lfs download from bitbucket failed (#31201) (#31329)
* Add nix flake for dev shell (#30967) (#31310)
* Fix and clean up `ConfirmModal` (#31283) (#31291)
* Optimize repo-list layout to enhance visual experience (#31272) (#31276)
* fixed the dropdown menu for the top New button to expand to the left (#31273) (#31275)
* Fix Activity Page Contributors dropdown (#31264) (#31269)
* fix: allow actions artifacts storage migration to complete succesfully (#31251) (#31257)
* Make blockquote attention recognize more syntaxes (#31240) (#31250)
* Remove .segment from .project-column (#31204) (#31239)
* Ignore FindRecentlyPushedNewBranches err (#31164) (#31171)
* Use vertical layout for multiple code expander buttons (#31122) (#31152)
* Remove duplicate `ProxyPreserveHost` in Apache httpd doc (#31143) (#31147)
* Improve mobile review ui (#31091) (#31136)
* Fix DashboardRepoList margin (#31121) (#31128)
* Update pip related commands for docker (#31106) (#31111)
## [1.22.0](https://github.com/go-gitea/gitea/releases/tag/v1.22.0) - 2024-05-27
This release stands as a monumental milestone in our development journey with a record-breaking incorporation of [1528](https://github.com/go-gitea/gitea/pulls?q=is%3Apr+milestone%3A1.22.0+is%3Amerged) pull requests. It marks the most extensive update in Gitea's history, showcasing a plethora of new features and infrastructure improvements.
+2 -2
View File
@@ -77,7 +77,7 @@ If your issue has not been reported yet, [open an issue](https://github.com/go-g
and answer the questions so we can understand and reproduce the problematic behavior. \
Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \
The more detailed and specific you are, the faster we can fix the issue. \
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://try.gitea.io>, as perhaps your problem has already been fixed on a current version. \
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://demo.gitea.com>, as perhaps your problem has already been fixed on a current version. \
Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
@@ -362,7 +362,7 @@ If you add a new feature or change an existing aspect of Gitea, the documentatio
## API v1
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
The API is documented by [swagger](https://gitea.com/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
### GitHub API compatibility
+4 -4
View File
@@ -2,11 +2,11 @@
FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
ENV GOPROXY=${GOPROXY:-direct}
ARG GITEA_VERSION
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata timetzdata $TAGS"
ENV TAGS="bindata timetzdata $TAGS"
ARG CGO_EXTRA_CFLAGS
# Build deps
@@ -72,8 +72,8 @@ RUN addgroup \
git && \
echo "git:*" | chpasswd -e
ENV USER git
ENV GITEA_CUSTOM /data/gitea
ENV USER=git
ENV GITEA_CUSTOM=/data/gitea
VOLUME ["/data"]
+8 -8
View File
@@ -2,11 +2,11 @@
FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
ENV GOPROXY=${GOPROXY:-direct}
ARG GITEA_VERSION
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata timetzdata $TAGS"
ENV TAGS="bindata timetzdata $TAGS"
ARG CGO_EXTRA_CFLAGS
#Build deps
@@ -75,14 +75,14 @@ COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_au
# git:git
USER 1000:1000
ENV GITEA_WORK_DIR /var/lib/gitea
ENV GITEA_CUSTOM /var/lib/gitea/custom
ENV GITEA_TEMP /tmp/gitea
ENV TMPDIR /tmp/gitea
ENV GITEA_WORK_DIR=/var/lib/gitea
ENV GITEA_CUSTOM=/var/lib/gitea/custom
ENV GITEA_TEMP=/tmp/gitea
ENV TMPDIR=/tmp/gitea
# TODO add to docs the ability to define the ini to load (useful to test and revert a config)
ENV GITEA_APP_INI /etc/gitea/app.ini
ENV HOME "/var/lib/gitea/git"
ENV GITEA_APP_INI=/etc/gitea/app.ini
ENV HOME="/var/lib/gitea/git"
VOLUME ["/var/lib/gitea", "/etc/gitea"]
WORKDIR /var/lib/gitea
+1 -1
View File
@@ -25,7 +25,7 @@ COMMA := ,
XGO_VERSION := go-1.22.x
AIR_PACKAGE ?= github.com/cosmtrek/air@v1
AIR_PACKAGE ?= github.com/air-verse/air@v1
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
+3 -3
View File
@@ -26,7 +26,7 @@ This project has been
[forked](https://blog.gitea.com/welcome-to-gitea/) from
[Gogs](https://gogs.io) since November of 2016, but a lot has changed.
For online demonstrations, you can visit [try.gitea.io](https://try.gitea.io).
For online demonstrations, you can visit [demo.gitea.com](https://demo.gitea.com).
For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login).
@@ -56,7 +56,7 @@ More info: https://docs.gitea.com/installation/install-from-source
./gitea web
> [!NOTE]
> If you're interested in using our APIs, we have experimental support with [documentation](https://try.gitea.io/api/swagger).
> If you're interested in using our APIs, we have experimental support with [documentation](https://docs.gitea.com/api).
## Contributing
@@ -80,7 +80,7 @@ https://docs.gitea.com/contributing/localization
## Further information
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://discourse.gitea.io/).
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
+1 -1
View File
@@ -18,7 +18,7 @@
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86amd64,还包括 ARM 和 PowerPC。
如果你想试用在线演示,请访问 [try.gitea.io](https://try.gitea.io/)。
如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
如果你想使用免费的 Gitea 服务(有仓库数量限制),请访问 [gitea.com](https://gitea.com/user/login)。
+16 -2
View File
@@ -5,7 +5,9 @@ package cmd
import (
"context"
"errors"
"fmt"
"io/fs"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
@@ -162,8 +164,20 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
_, err := storage.Copy(dstStorage, artifact.ArtifactPath, storage.ActionsArtifacts, artifact.ArtifactPath)
return err
if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
return nil
}
_, err := storage.Copy(dstStorage, artifact.StoragePath, storage.ActionsArtifacts, artifact.StoragePath)
if err != nil {
// ignore files that do not exist
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
return nil
})
}
+1 -4
View File
@@ -1,8 +1,5 @@
# Gitea: Docs
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
These docs are ingested by our [docs repo](https://gitea.com/gitea/gitea-docusaurus).
## Authors
@@ -18,5 +15,5 @@ for the full license text.
## Copyright
```
Copyright (c) 2016 The Gitea Authors <https://gitea.io>
Copyright (c) 2016 The Gitea Authors
```
+1 -5
View File
@@ -1,9 +1,5 @@
# Gitea: 文档
[![Build Status](http://drone.gitea.io/api/badges/go-gitea/docs/status.svg)](http://drone.gitea.io/go-gitea/docs)
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
https://gitea.com/gitea/gitea-docusaurus
## 关于我们
@@ -18,5 +14,5 @@ https://gitea.com/gitea/gitea-docusaurus
## 版权声明
```
Copyright (c) 2016 The Gitea Authors <https://gitea.io>
Copyright (c) 2016 The Gitea Authors
```
@@ -38,12 +38,10 @@ FROM gitea/gitea:@version@
COPY custom/app.ini /data/gitea/conf/app.ini
[...]
RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev py-pip python3-dev py3-pip py3-pyzmq
RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev pandoc python3-dev py3-pyzmq pipx
# install any other package you need for your external renderers
RUN pip3 install --upgrade pip
RUN pip3 install -U setuptools
RUN pip3 install jupyter docutils
RUN pipx install jupyter docutils --include-deps
# add above any other python package you may need to install
```
@@ -37,12 +37,10 @@ FROM gitea/gitea:@version@
COPY custom/app.ini /data/gitea/conf/app.ini
[...]
RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev py-pip python3-dev py3-pip py3-pyzmq
RUN apk --no-cache add asciidoctor freetype freetype-dev gcc g++ libpng libffi-dev pandoc python3-dev py3-pyzmq pipx
# 安装其他您需要的外部渲染器的软件包
RUN pip3 install --upgrade pip
RUN pip3 install -U setuptools
RUN pip3 install jupyter docutils
RUN pipx install jupyter docutils --include-deps
# 在上面添加您需要安装的任何其他 Python 软件包
```
@@ -169,7 +169,6 @@ If you want Apache HTTPD to serve your Gitea instance, you can add the following
ProxyRequests off
AllowEncodedSlashes NoDecode
ProxyPass / http://localhost:3000/ nocanon
ProxyPreserveHost On
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>
```
+1 -1
View File
@@ -117,7 +117,7 @@ curl -v "http://localhost/api/v1/repos/search?limit=1"
API Reference guide is auto-generated by swagger and available on:
`https://gitea.your.host/api/swagger`
or on the
[Gitea demo instance](https://try.gitea.io/api/swagger)
[Gitea instance](https://gitea.com/api/swagger)
The OpenAPI document is at:
`https://gitea.your.host/swagger.v1.json`
+3 -3
View File
@@ -45,7 +45,7 @@ To migrate from GitHub to Gitea, you can use Gitea's built-in migration form.
In order to migrate items such as issues, pull requests, etc. you will need to input at least your username.
[Example (requires login)](https://try.gitea.io/repo/migrate)
[Example (requires login)](https://demo.gitea.com/repo/migrate)
To migrate from GitLab to Gitea, you can use this non-affiliated tool:
@@ -137,9 +137,9 @@ All Gitea instances have the built-in API and there is no way to disable it comp
You can, however, disable showing its documentation by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`.
For more information, refer to Gitea's [API docs](development/api-usage.md).
You can see the latest API (for example) on https://try.gitea.io/api/swagger
You can see the latest API (for example) on https://gitea.com/api/swagger
You can also see an example of the `swagger.json` file at https://try.gitea.io/swagger.v1.json
You can also see an example of the `swagger.json` file at https://gitea.com/swagger.v1.json
## Adjusting your server for public/private use
+3 -3
View File
@@ -47,7 +47,7 @@ menu:
为了迁移诸如问题、拉取请求等项目,您需要至少输入您的用户名。
[Example (requires login)](https://try.gitea.io/repo/migrate)
[Example (requires login)](https://demo.gitea.com/repo/migrate)
要从GitLab迁移到Gitea,您可以使用这个非关联的工具:
@@ -141,9 +141,9 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
但是,您可以在app.ini的api部分将ENABLE_SWAGGER设置为false,以禁用其文档显示。
有关更多信息,请参阅Gitea的[API文档](development/api-usage.md)。
您可以在上查看最新的API(例如)https://try.gitea.io/api/swagger
您可以在上查看最新的API(例如)https://gitea.com/api/swagger
您还可以在上查看`swagger.json`文件的示例 https://try.gitea.io/swagger.v1.json
您还可以在上查看`swagger.json`文件的示例 https://gitea.com/swagger.v1.json
## 调整服务器用于公共/私有使用
+3 -3
View File
@@ -19,11 +19,11 @@ menu:
- [Paid Commercial Support](https://about.gitea.com/)
- [Discord](https://discord.gg/Gitea)
- [Discourse Forum](https://discourse.gitea.io/)
- [Forum](https://forum.gitea.com/)
- [Matrix](https://matrix.to/#/#gitea-space:matrix.org)
- NOTE: Most of the Matrix channels are bridged with their counterpart in Discord and may experience some degree of flakiness with the bridge process.
- Chinese Support
- [Discourse Chinese Category](https://discourse.gitea.io/c/5-category/5)
- [Discourse Chinese Category](https://forum.gitea.com/c/5-category/5)
- QQ Group 328432459
# Bug Report
@@ -39,7 +39,7 @@ If you found a bug, please [create an issue on GitHub](https://github.com/go-git
- When using systemd, use `journalctl --lines 1000 --unit gitea` to collect logs.
- When using docker, use `docker logs --tail 1000 <gitea-container>` to collect logs.
4. Reproducible steps so that others could reproduce and understand the problem more quickly and easily.
- [try.gitea.io](https://try.gitea.io) could be used to reproduce the problem.
- [demo.gitea.com](https://demo.gitea.com) could be used to reproduce the problem.
5. If you encounter slow/hanging/deadlock problems, please report the stacktrace when the problem occurs.
Go to the "Site Admin" -> "Monitoring" -> "Stacktrace" -> "Download diagnosis report".
+3 -3
View File
@@ -19,11 +19,11 @@ menu:
- [付费商业支持](https://about.gitea.com/)
- [Discord](https://discord.gg/Gitea)
- [Discourse 论坛](https://discourse.gitea.io/)
- [论坛](https://forum.gitea.com/)
- [Matrix](https://matrix.to/#/#gitea-space:matrix.org)
- 注意:大多数 Matrix 频道都与 Discord 中的对应频道桥接,可能在桥接过程中会出现一定程度的不稳定性。
- 中文支持
- [Discourse 中文分类](https://discourse.gitea.io/c/5-category/5)
- [Discourse 中文分类](https://forum.gitea.com/c/5-category/5)
- QQ 群 328432459
# Bug 报告
@@ -39,7 +39,7 @@ menu:
- 在使用 systemd 时,使用 `journalctl --lines 1000 --unit gitea` 收集日志。
- 在使用 Docker 时,使用 `docker logs --tail 1000 <gitea-container>` 收集日志。
4. 可重现的步骤,以便他人能够更快速、更容易地重现和理解问题。
- [try.gitea.io](https://try.gitea.io) 可用于重现问题。
- [demo.gitea.com](https://demo.gitea.com) 可用于重现问题。
5. 如果遇到慢速/挂起/死锁等问题,请在出现问题时报告堆栈跟踪。
转到 "Site Admin" -> "Monitoring" -> "Stacktrace" -> "Download diagnosis report"。
+1 -1
View File
@@ -21,7 +21,7 @@ up a self-hosted Git service.
With Go, this can be done platform-independently across
**all platforms** which Go supports, including Linux, macOS, and Windows,
on x86, amd64, ARM and PowerPC architectures.
You can try it out using [the online demo](https://try.gitea.io/).
You can try it out using [the online demo](https://demo.gitea.com).
## Features
@@ -5,11 +5,9 @@ slug: "badge"
sidebar_position: 11
toc: false
draft: false
aliases:
- /en-us/badge
menu:
sidebar:
parent: "usage"
parent: "actions"
name: "Badge"
sidebar_position: 11
identifier: "Badge"
@@ -27,7 +25,7 @@ It is designed to be compatible with [GitHub Actions workflow badge](https://doc
You can use the following URL to get the badge:
```
https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file}?branch={branch}&event={event}
https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file}/badge.svg?branch={branch}&event={event}
```
- `{owner}`: The owner of the repository.
@@ -5,11 +5,9 @@ slug: "secrets"
sidebar_position: 50
draft: false
toc: false
aliases:
- /en-us/secrets
menu:
sidebar:
parent: "usage"
parent: "actions"
name: "Secrets"
sidebar_position: 50
identifier: "usage-secrets"
@@ -5,11 +5,9 @@ slug: "secrets"
sidebar_position: 50
draft: false
toc: false
aliases:
- /zh-cn/secrets
menu:
sidebar:
parent: "usage"
parent: "actions"
name: "密钥管理"
sidebar_position: 50
identifier: "usage-secrets"
+1 -1
View File
@@ -236,7 +236,7 @@ configure this, set the fields below:
- Restrict what domains can log in if using a public SMTP host or SMTP host
with multiple domains.
- Example: `gitea.io,mydomain.com,mydomain2.com`
- Example: `gitea.com,mydomain.com,mydomain2.com`
- Force SMTPS
+1 -1
View File
@@ -194,7 +194,7 @@ PAM提供了一种机制,通过对用户进行PAM认证来自动将其添加
- 如果使用公共 SMTP 主机或有多个域的 SMTP 主机,限制哪些域可以登录
限制哪些域可以登录。
- 示例: `gitea.io,mydomain.com,mydomain2.com`
- 示例: `gitea.com,mydomain.com,mydomain2.com`
- 强制使用 SMTPS
- 默认情况下将使用SMTPS连接到端口465.如果您希望将smtp用于其他端口,自行设置
@@ -308,7 +308,7 @@ This is a example for a issue config file
blank_issues_enabled: true
contact_links:
- name: Gitea
url: https://gitea.io
url: https://gitea.com
about: Visit the Gitea Website
```
Generated
+61
View File
@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1715534503,
"narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+37
View File
@@ -0,0 +1,37 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# generic
git
git-lfs
gnumake
gnused
gnutar
gzip
# frontend
nodejs_20
# linting
python312
poetry
# backend
go_1_22
];
};
}
);
}
+12 -11
View File
@@ -56,7 +56,7 @@ require (
github.com/google/go-github/v57 v57.0.0
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.1.2
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.2.2
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
@@ -104,13 +104,13 @@ require (
github.com/yuin/goldmark v1.7.0
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.22.0
golang.org/x/image v0.15.0
golang.org/x/net v0.24.0
golang.org/x/crypto v0.23.0
golang.org/x/image v0.18.0
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.18.0
golang.org/x/sys v0.19.0
golang.org/x/text v0.14.0
golang.org/x/tools v0.19.0
golang.org/x/sys v0.20.0
golang.org/x/text v0.16.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.33.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -210,7 +210,7 @@ require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -288,8 +288,8 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
@@ -304,7 +304,8 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142
replace github.com/nektos/act => gitea.com/gitea/act v0.259.1
replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
exclude github.com/gofrs/uuid v3.2.0+incompatible
+26 -26
View File
@@ -88,6 +88,8 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58PaB6aA=
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
@@ -418,6 +420,8 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@@ -432,11 +436,10 @@ github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTj
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -554,8 +557,6 @@ github.com/meilisearch/meilisearch-go v0.26.2 h1:3gTlmiV1dHHumVUhYdJbvh3camiNiyq
github.com/meilisearch/meilisearch-go v0.26.2/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs=
@@ -799,8 +800,6 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 h1:3seWKGVhGoc66Ht5QlhQsr4xT2caDnFegsnh2NqvENU=
github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@@ -854,20 +853,20 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -888,8 +887,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -900,8 +899,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -939,8 +938,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -950,8 +949,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -963,8 +962,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -979,8 +979,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
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=
+5 -4
View File
@@ -286,13 +286,14 @@ type UserIDCount struct {
Count int64
}
// GetUIDsAndNotificationCounts between the two provided times
// GetUIDsAndNotificationCounts returns the unread counts for every user between the two provided times.
// It must return all user IDs which appear during the period, including count=0 for users who have read all.
func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) {
sql := `SELECT user_id, count(*) AS count FROM notification ` +
sql := `SELECT user_id, sum(case when status= ? then 1 else 0 end) AS count FROM notification ` +
`WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` +
`updated_unix < ?) AND status = ? GROUP BY user_id`
`updated_unix < ?) GROUP BY user_id`
var res []UserIDCount
return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res)
return res, db.GetEngine(ctx).SQL(sql, NotificationStatusUnread, since, until).Find(&res)
}
// SetIssueReadBy sets issue to be read by given user.
+4 -8
View File
@@ -107,17 +107,13 @@ func (opts FindBranchOptions) ToConds() builder.Cond {
func (opts FindBranchOptions) ToOrders() string {
orderBy := opts.OrderBy
if opts.IsDeletedBranch.ValueOrDefault(true) { // if deleted branch included, put them at the end
if orderBy != "" {
orderBy += ", "
}
orderBy += "is_deleted ASC"
}
if orderBy == "" {
// the commit_time might be the same, so add the "name" to make sure the order is stable
return "commit_time DESC, name ASC"
orderBy = "commit_time DESC, name ASC"
}
if opts.IsDeletedBranch.ValueOrDefault(true) { // if deleted branch included, put them at the beginning
orderBy = "is_deleted ASC, " + orderBy
}
return orderBy
}
+1 -1
View File
@@ -92,7 +92,7 @@ func addObjectFormatNameToRepository(x *xorm.Engine) error {
// Here to catch weird edge-cases where column constraints above are
// not applied by the DB backend
_, err := x.Exec("UPDATE repository set object_format_name = 'sha1' WHERE object_format_name = '' or object_format_name IS NULL")
_, err := x.Exec("UPDATE `repository` set `object_format_name` = 'sha1' WHERE `object_format_name` = '' or `object_format_name` IS NULL")
return err
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestRepoAvatarLink(t *testing.T) {
defer test.MockVariableValue(&setting.AppURL, "https://localhost/")()
defer test.MockVariableValue(&setting.AppSubURL, "")()
repo := &Repository{ID: 1, Avatar: "avatar.png"}
link := repo.AvatarLink(db.DefaultContext)
assert.Equal(t, "https://localhost/repo-avatars/avatar.png", link)
setting.AppURL = "https://localhost/sub-path/"
setting.AppSubURL = "/sub-path"
link = repo.AvatarLink(db.DefaultContext)
assert.Equal(t, "https://localhost/sub-path/repo-avatars/avatar.png", link)
}
+4 -2
View File
@@ -89,9 +89,11 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size)
}
// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
// AvatarLink returns the full avatar url with http host.
// TODO: refactor it to a relative URL, but it is still used in API response at the moment
func (u *User) AvatarLink(ctx context.Context) string {
return httplib.MakeAbsoluteURL(ctx, u.AvatarLinkWithSize(ctx, 0))
relLink := u.AvatarLinkWithSize(ctx, 0) // it can't be empty
return httplib.MakeAbsoluteURL(ctx, relLink)
}
// IsUploadAvatarChanged returns true if the current user's avatar would be changed with the provided data
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestUserAvatarLink(t *testing.T) {
defer test.MockVariableValue(&setting.AppURL, "https://localhost/")()
defer test.MockVariableValue(&setting.AppSubURL, "")()
u := &User{ID: 1, Avatar: "avatar.png"}
link := u.AvatarLink(db.DefaultContext)
assert.Equal(t, "https://localhost/avatars/avatar.png", link)
setting.AppURL = "https://localhost/sub-path/"
setting.AppSubURL = "/sub-path"
link = u.AvatarLink(db.DefaultContext)
assert.Equal(t, "https://localhost/sub-path/avatars/avatar.png", link)
}
+56 -1
View File
@@ -4,12 +4,67 @@
package base
import (
"unicode/utf8"
"golang.org/x/text/collate"
"golang.org/x/text/language"
)
func naturalSortGetRune(str string, pos int) (r rune, size int, has bool) {
if pos >= len(str) {
return 0, 0, false
}
r, size = utf8.DecodeRuneInString(str[pos:])
if r == utf8.RuneError {
r, size = rune(str[pos]), 1 // if invalid input, treat it as a single byte ascii
}
return r, size, true
}
func naturalSortAdvance(str string, pos int) (end int, isNumber bool) {
end = pos
for {
r, size, has := naturalSortGetRune(str, end)
if !has {
break
}
isCurRuneNum := '0' <= r && r <= '9'
if end == pos {
isNumber = isCurRuneNum
end += size
} else if isCurRuneNum == isNumber {
end += size
} else {
break
}
}
return end, isNumber
}
// NaturalSortLess compares two strings so that they could be sorted in natural order
func NaturalSortLess(s1, s2 string) bool {
// There is a bug in Golang's collate package: https://github.com/golang/go/issues/67997
// text/collate: CompareString(collate.Numeric) returns wrong result for "0.0" vs "1.0" #67997
// So we need to handle the number parts by ourselves
c := collate.New(language.English, collate.Numeric)
return c.CompareString(s1, s2) < 0
pos1, pos2 := 0, 0
for pos1 < len(s1) && pos2 < len(s2) {
end1, isNum1 := naturalSortAdvance(s1, pos1)
end2, isNum2 := naturalSortAdvance(s2, pos2)
part1, part2 := s1[pos1:end1], s2[pos2:end2]
if isNum1 && isNum2 {
if part1 != part2 {
if len(part1) != len(part2) {
return len(part1) < len(part2)
}
return part1 < part2
}
} else {
if cmp := c.CompareString(part1, part2); cmp != 0 {
return cmp < 0
}
}
pos1, pos2 = end1, end2
}
return len(s1) < len(s2)
}
+29 -14
View File
@@ -10,21 +10,36 @@ import (
)
func TestNaturalSortLess(t *testing.T) {
test := func(s1, s2 string, less bool) {
assert.Equal(t, less, NaturalSortLess(s1, s2), "s1=%q, s2=%q", s1, s2)
testLess := func(s1, s2 string) {
assert.True(t, NaturalSortLess(s1, s2), "s1<s2 should be true: s1=%q, s2=%q", s1, s2)
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
}
testEqual := func(s1, s2 string) {
assert.False(t, NaturalSortLess(s1, s2), "s1<s2 should be false: s1=%q, s2=%q", s1, s2)
assert.False(t, NaturalSortLess(s2, s1), "s2<s1 should be false: s1=%q, s2=%q", s1, s2)
}
test("v1.20.0", "v1.2.0", false)
test("v1.20.0", "v1.29.0", true)
test("v1.20.0", "v1.20.0", false)
test("abc", "bcd", true)
test("a-1-a", "a-1-b", true)
test("2", "12", true)
test("a", "ab", true)
test("A", "b", true)
test("a", "B", true)
testEqual("", "")
testLess("", "a")
testLess("", "1")
test("cafe", "café", true)
test("café", "cafe", false)
test("caff", "café", false)
testLess("v1.2", "v1.2.0")
testLess("v1.2.0", "v1.10.0")
testLess("v1.20.0", "v1.29.0")
testEqual("v1.20.0", "v1.20.0")
testLess("a", "A")
testLess("a", "B")
testLess("A", "b")
testLess("A", "ab")
testLess("abc", "bcd")
testLess("a-1-a", "a-1-b")
testLess("2", "12")
testLess("cafe", "café")
testLess("café", "caff")
testLess("A-2", "A-11")
testLess("0.txt", "1.txt")
}
+2
View File
@@ -8,6 +8,8 @@ import (
"time"
"code.gitea.io/gitea/modules/setting"
_ "gitea.com/go-chi/cache/memcache" //nolint:depguard // memcache plugin for cache, it is required for config "ADAPTER=memcache"
)
var defaultCache StringCache
+20 -8
View File
@@ -57,11 +57,16 @@ func getForwardedHost(req *http.Request) string {
return req.Header.Get("X-Forwarded-Host")
}
// GuessCurrentAppURL tries to guess the current full URL by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
func GuessCurrentAppURL(ctx context.Context) string {
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
}
// GuessCurrentHostURL tries to guess the current full host URL (no sub-path) by http headers, there is no trailing slash.
func GuessCurrentHostURL(ctx context.Context) string {
req, ok := ctx.Value(RequestContextKey).(*http.Request)
if !ok {
return setting.AppURL
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
}
// If no scheme provided by reverse proxy, then do not guess the AppURL, use the configured one.
// At the moment, if site admin doesn't configure the proxy headers correctly, then Gitea would guess wrong.
@@ -74,20 +79,27 @@ func GuessCurrentAppURL(ctx context.Context) string {
// So in the future maybe it should introduce a new config option, to let site admin decide how to guess the AppURL.
reqScheme := getRequestScheme(req)
if reqScheme == "" {
return setting.AppURL
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
}
reqHost := getForwardedHost(req)
if reqHost == "" {
reqHost = req.Host
}
return reqScheme + "://" + reqHost + setting.AppSubURL + "/"
return reqScheme + "://" + reqHost
}
func MakeAbsoluteURL(ctx context.Context, s string) string {
if IsRelativeURL(s) {
return GuessCurrentAppURL(ctx) + strings.TrimPrefix(s, "/")
// MakeAbsoluteURL tries to make a link to an absolute URL:
// * If link is empty, it returns the current app URL.
// * If link is absolute, it returns the link.
// * Otherwise, it returns the current host URL + link, the link itself should have correct sub-path (AppSubURL) if needed.
func MakeAbsoluteURL(ctx context.Context, link string) string {
if link == "" {
return GuessCurrentAppURL(ctx)
}
return s
if !IsRelativeURL(link) {
return link
}
return GuessCurrentHostURL(ctx) + "/" + strings.TrimPrefix(link, "/")
}
func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
+5 -5
View File
@@ -46,14 +46,14 @@ func TestMakeAbsoluteURL(t *testing.T) {
ctx := context.Background()
assert.Equal(t, "http://cfg-host/sub/", MakeAbsoluteURL(ctx, ""))
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "foo"))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
assert.Equal(t, "http://other/foo", MakeAbsoluteURL(ctx, "http://other/foo"))
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
Host: "user-host",
})
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
Host: "user-host",
@@ -61,7 +61,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
"X-Forwarded-Host": {"forwarded-host"},
},
})
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
Host: "user-host",
@@ -70,7 +70,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
"X-Forwarded-Proto": {"https"},
},
})
assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
assert.Equal(t, "https://forwarded-host/foo", MakeAbsoluteURL(ctx, "/foo"))
}
func TestIsCurrentGiteaSiteURL(t *testing.T) {
+6 -1
View File
@@ -38,6 +38,12 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.MilestoneIDs = opts.MilestoneIDs
}
if opts.ProjectID > 0 {
searchOpt.ProjectID = optional.Some(opts.ProjectID)
} else if opts.ProjectID == -1 { // FIXME: this is inconsistent from other places
searchOpt.ProjectID = optional.Some[int64](0) // Those issues with no project(projectid==0)
}
// See the comment of issues_model.SearchOptions for the reason why we need to convert
convertID := func(id int64) optional.Option[int64] {
if id > 0 {
@@ -49,7 +55,6 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
return nil
}
searchOpt.ProjectID = convertID(opts.ProjectID)
searchOpt.ProjectBoardID = convertID(opts.ProjectBoardID)
searchOpt.PosterID = convertID(opts.PosterID)
searchOpt.AssigneeID = convertID(opts.AssigneeID)
+2 -2
View File
@@ -211,7 +211,7 @@ func createRequest(ctx context.Context, method, url string, headers map[string]s
for key, value := range headers {
req.Header.Set(key, value)
}
req.Header.Set("Accept", MediaType)
req.Header.Set("Accept", AcceptHeader)
return req, nil
}
@@ -251,6 +251,6 @@ func handleErrorResponse(resp *http.Response) error {
return err
}
log.Trace("ErrorResponse: %v", er)
log.Trace("ErrorResponse(%v): %v", resp.Status, er)
return errors.New(er.Message)
}
+2 -2
View File
@@ -155,7 +155,7 @@ func TestHTTPClientDownload(t *testing.T) {
hc := &http.Client{Transport: RoundTripFunc(func(req *http.Request) *http.Response {
assert.Equal(t, "POST", req.Method)
assert.Equal(t, MediaType, req.Header.Get("Content-type"))
assert.Equal(t, MediaType, req.Header.Get("Accept"))
assert.Equal(t, AcceptHeader, req.Header.Get("Accept"))
var batchRequest BatchRequest
err := json.NewDecoder(req.Body).Decode(&batchRequest)
@@ -263,7 +263,7 @@ func TestHTTPClientUpload(t *testing.T) {
hc := &http.Client{Transport: RoundTripFunc(func(req *http.Request) *http.Response {
assert.Equal(t, "POST", req.Method)
assert.Equal(t, MediaType, req.Header.Get("Content-type"))
assert.Equal(t, MediaType, req.Header.Get("Accept"))
assert.Equal(t, AcceptHeader, req.Header.Get("Accept"))
var batchRequest BatchRequest
err := json.NewDecoder(req.Body).Decode(&batchRequest)
+2
View File
@@ -10,6 +10,8 @@ import (
const (
// MediaType contains the media type for LFS server requests
MediaType = "application/vnd.git-lfs+json"
// Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
)
// BatchRequest contains multiple requests processed in one batch operation.
+1
View File
@@ -37,6 +37,7 @@ func (a *BasicTransferAdapter) Download(ctx context.Context, l *Link) (io.ReadCl
if err != nil {
return nil, err
}
log.Debug("Download Request: %+v", req)
resp, err := performRequest(ctx, a.client, req)
if err != nil {
return nil, err
+1 -1
View File
@@ -26,7 +26,7 @@ func TestBasicTransferAdapter(t *testing.T) {
p := Pointer{Oid: "b5a2c96250612366ea272ffac6d9744aaf4b45aacd96aa7cfcb931ee3b558259", Size: 5}
roundTripHandler := func(req *http.Request) *http.Response {
assert.Equal(t, MediaType, req.Header.Get("Accept"))
assert.Equal(t, AcceptHeader, req.Header.Get("Accept"))
assert.Equal(t, "test-value", req.Header.Get("test-header"))
url := req.URL.String()
+20 -44
View File
@@ -49,7 +49,7 @@ var (
// hashCurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
// Although SHA1 hashes are 40 chars long, SHA256 are 64, the regex matches the hash from 7 to 64 chars in length
// so that abbreviated hash links can be used as well. This matches git and GitHub usability.
hashCurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,64})(?:\s|$|\)|\]|[.,](\s|$))`)
hashCurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,64})(?:\s|$|\)|\]|[.,:](\s|$))`)
// shortLinkPattern matches short but difficult to parse [[name|link|arg=test]] syntax
shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
@@ -88,6 +88,10 @@ func IsFullURLString(link string) bool {
return fullURLPattern.MatchString(link)
}
func IsNonEmptyRelativePath(link string) bool {
return link != "" && !IsFullURLString(link) && link[0] != '/' && link[0] != '?' && link[0] != '#'
}
// regexp for full links to issues/pulls
var issueFullPattern *regexp.Regexp
@@ -372,7 +376,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
return nil
}
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Node {
// Add user-content- to IDs and "#" links if they don't already have them
for idx, attr := range node.Attr {
val := strings.TrimPrefix(attr.Val, "#")
@@ -391,27 +395,20 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
}
}
// We ignore code and pre.
switch node.Type {
case html.TextNode:
textNode(ctx, procs, node)
case html.ElementNode:
if node.Data == "img" {
for i, attr := range node.Attr {
if attr.Key != "src" {
continue
}
if len(attr.Val) > 0 && !IsFullURLString(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
}
attr.Val = camoHandleLink(attr.Val)
node.Attr[i] = attr
}
if node.Data == "code" || node.Data == "pre" {
// ignore code and pre nodes
return node.NextSibling
} else if node.Data == "img" {
return visitNodeImg(ctx, node)
} else if node.Data == "video" {
return visitNodeVideo(ctx, node)
} else if node.Data == "a" {
// Restrict text in links to emojis
procs = emojiProcessors
} else if node.Data == "code" || node.Data == "pre" {
return
} else if node.Data == "i" {
for _, attr := range node.Attr {
if attr.Key != "class" {
@@ -434,11 +431,11 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
}
}
}
for n := node.FirstChild; n != nil; n = n.NextSibling {
visitNode(ctx, procs, n)
for n := node.FirstChild; n != nil; {
n = visitNode(ctx, procs, n)
}
}
// ignore everything else
return node.NextSibling
}
// textNode runs the passed node through various processors, in order to handle
@@ -733,10 +730,10 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
if image {
link = strings.ReplaceAll(link, " ", "+")
} else {
link = strings.ReplaceAll(link, " ", "-")
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
}
if !strings.Contains(link, "/") {
link = url.PathEscape(link)
link = url.PathEscape(link) // FIXME: it doesn't seem right and it might cause double-escaping
}
}
if image {
@@ -768,28 +765,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
childNode.Attr = childNode.Attr[:2]
}
} else {
if !absoluteLink {
var base string
if ctx.IsWiki {
switch ext {
case "":
// no file extension, create a regular wiki link
base = ctx.Links.WikiLink()
default:
// we have a file extension:
// return a regular wiki link if it's a renderable file (extension),
// raw link otherwise
if Type(link) != "" {
base = ctx.Links.WikiLink()
} else {
base = ctx.Links.WikiRawLink()
}
}
} else {
base = ctx.Links.SrcLink()
}
link = util.URLJoin(base, link)
}
link, _ = ResolveLink(ctx, link, "")
childNode.Type = html.TextNode
childNode.Data = name
}
@@ -851,7 +827,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered?
// The "mode" approach should be refactored to some other more clear&reliable way.
crossLinkOnly := (ctx.Metas["mode"] == "document" && !ctx.IsWiki)
crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
var (
found bool
+10 -10
View File
@@ -18,8 +18,7 @@ import (
const (
TestAppURL = "http://localhost:3000/"
TestOrgRepo = "gogits/gogs"
TestRepoURL = TestAppURL + TestOrgRepo + "/"
TestRepoURL = TestAppURL + "test-owner/test-repo/"
)
// externalIssueLink an HTML link to an alphanumeric-style issue
@@ -64,8 +63,8 @@ var regexpMetas = map[string]string{
// these values should match the TestOrgRepo const above
var localMetas = map[string]string{
"user": "gogits",
"repo": "gogs",
"user": "test-owner",
"repo": "test-repo",
}
func TestRender_IssueIndexPattern(t *testing.T) {
@@ -362,12 +361,12 @@ func TestRender_FullIssueURLs(t *testing.T) {
`Look here <a href="http://localhost:3000/person/repo/issues/4" class="ref-issue">person/repo#4</a>`)
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`)
test("http://localhost:3000/gogits/gogs/issues/4",
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a>`)
test("http://localhost:3000/gogits/gogs/issues/4 test",
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a> test`)
test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
`<a href="http://localhost:3000/gogits/gogs/issues/4?a=1&amp;b=2#comment-123" class="ref-issue">#4 (comment)</a> test`)
test("http://localhost:3000/test-owner/test-repo/issues/4",
`<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a>`)
test("http://localhost:3000/test-owner/test-repo/issues/4 test",
`<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a> test`)
test("http://localhost:3000/test-owner/test-repo/issues/4?a=1&b=2#comment-123 test",
`<a href="http://localhost:3000/test-owner/test-repo/issues/4?a=1&amp;b=2#comment-123" class="ref-issue">#4 (comment)</a> test`)
test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24",
"http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24")
test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files",
@@ -381,6 +380,7 @@ func TestRegExp_sha1CurrentPattern(t *testing.T) {
"(abcdefabcdefabcdefabcdefabcdefabcdefabcd)",
"[abcdefabcdefabcdefabcdefabcdefabcdefabcd]",
"abcdefabcdefabcdefabcdefabcdefabcdefabcd.",
"abcdefabcdefabcdefabcdefabcdefabcdefabcd:",
}
falseTestCases := []string{
"test",
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"path"
"code.gitea.io/gitea/modules/util"
)
func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
isAnchorFragment := link != "" && link[0] == '#'
if !isAnchorFragment && !IsFullURLString(link) {
linkBase := ctx.Links.Base
if ctx.IsWiki {
if ext := path.Ext(link); ext == "" || ext == ".-" {
linkBase = ctx.Links.WikiLink() // the link is for a wiki page
} else if DetectMarkupTypeByFileName(link) != "" {
linkBase = ctx.Links.WikiLink() // the link is renderable as a wiki page
} else {
linkBase = ctx.Links.WikiRawLink() // otherwise, use a raw link instead to view&download medias
}
} else if ctx.Links.BranchPath != "" || ctx.Links.TreePath != "" {
// if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
// and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}"
linkBase = ctx.Links.SrcLink()
}
link, resolved = util.URLJoin(linkBase, link), true
}
if isAnchorFragment && userContentAnchorPrefix != "" {
link, resolved = userContentAnchorPrefix+link[1:], true
}
return link, resolved
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
)
func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
next = img.NextSibling
for i, attr := range img.Attr {
if attr.Key != "src" {
continue
}
if IsNonEmptyRelativePath(attr.Val) {
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
// By default, the "<img>" tag should also be clickable,
// because frontend use `<img>` to paste the re-scaled image into the markdown,
// so it must match the default markdown image behavior.
hasParentAnchor := false
for p := img.Parent; p != nil; p = p.Parent {
if hasParentAnchor = p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
break
}
}
if !hasParentAnchor {
imgA := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
{Key: "href", Val: attr.Val},
{Key: "target", Val: "_blank"},
}}
parent := img.Parent
imgNext := img.NextSibling
parent.RemoveChild(img)
parent.InsertBefore(imgA, imgNext)
imgA.AppendChild(img)
}
}
attr.Val = camoHandleLink(attr.Val)
img.Attr[i] = attr
}
return next
}
func visitNodeVideo(ctx *RenderContext, node *html.Node) (next *html.Node) {
next = node.NextSibling
for i, attr := range node.Attr {
if attr.Key != "src" {
continue
}
if IsNonEmptyRelativePath(attr.Val) {
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
}
attr.Val = camoHandleLink(attr.Val)
node.Attr[i] = attr
}
return next
}
+43 -41
View File
@@ -53,7 +53,7 @@ func TestRender_Commits(t *testing.T) {
}
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
repo := markup.TestRepoURL
repo := "http://localhost:3000/gogits/gogs"
commit := util.URLJoin(repo, "commit", sha)
tree := util.URLJoin(repo, "tree", sha, "src")
@@ -107,8 +107,8 @@ func TestRender_CrossReferences(t *testing.T) {
}
test(
"gogits/gogs#12345",
`<p><a href="`+util.URLJoin(markup.TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
"test-owner/test-repo#12345",
`<p><a href="`+util.URLJoin(markup.TestAppURL, "test-owner", "test-repo", "issues", "12345")+`" class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p>`)
test(
"go-gitea/gitea#12345",
`<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
@@ -156,13 +156,18 @@ func TestRender_links(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
// Text that should be turned into URL
defaultCustom := setting.Markdown.CustomURLSchemes
oldCustomURLSchemes := setting.Markdown.CustomURLSchemes
markup.ResetDefaultSanitizerForTesting()
defer func() {
setting.Markdown.CustomURLSchemes = oldCustomURLSchemes
markup.ResetDefaultSanitizerForTesting()
markup.CustomLinkURLSchemes(oldCustomURLSchemes)
}()
setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"}
markup.InitializeSanitizer()
markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
// Text that should be turned into URL
test(
"https://www.example.com",
`<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`)
@@ -246,11 +251,6 @@ func TestRender_links(t *testing.T) {
test(
"ftps://gitea.com",
`<p>ftps://gitea.com</p>`)
// Restore previous settings
setting.Markdown.CustomURLSchemes = defaultCustom
markup.InitializeSanitizer()
markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
func TestRender_email(t *testing.T) {
@@ -442,6 +442,10 @@ func TestRender_ShortLinks(t *testing.T) {
"[[Link]]",
`<p><a href="`+url+`" rel="nofollow">Link</a></p>`,
`<p><a href="`+urlWiki+`" rel="nofollow">Link</a></p>`)
test(
"[[Link.-]]",
`<p><a href="http://localhost:3000/test-owner/test-repo/src/master/Link.-" rel="nofollow">Link.-</a></p>`,
`<p><a href="http://localhost:3000/test-owner/test-repo/wiki/Link.-" rel="nofollow">Link.-</a></p>`)
test(
"[[Link.jpg]]",
`<p><a href="`+imgurl+`" rel="nofollow"><img src="`+imgurl+`" title="Link.jpg" alt="Link.jpg"/></a></p>`,
@@ -516,44 +520,41 @@ func TestRender_ShortLinks(t *testing.T) {
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
}
func TestRender_RelativeImages(t *testing.T) {
setting.AppURL = markup.TestAppURL
test := func(input, expected, expectedWiki string) {
func TestRender_RelativeMedias(t *testing.T) {
render := func(input string, isWiki bool, links markup.Links) string {
buffer, err := markdown.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
Links: markup.Links{
Base: markup.TestRepoURL,
BranchPath: "master",
},
Metas: localMetas,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
buffer, err = markdown.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
Links: markup.Links{
Base: markup.TestRepoURL,
},
Ctx: git.DefaultContext,
Links: links,
Metas: localMetas,
IsWiki: true,
IsWiki: isWiki,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
return strings.TrimSpace(string(buffer))
}
rawwiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw")
mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
out := render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
assert.Equal(t, `<a href="/test-owner/test-repo/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/LINK"/></a>`, out)
test(
`<img src="Link">`,
`<img src="`+util.URLJoin(mediatree, "Link")+`"/>`,
`<img src="`+util.URLJoin(rawwiki, "Link")+`"/>`)
out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"})
assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out)
test(
`<img src="./icon.png">`,
`<img src="`+util.URLJoin(mediatree, "icon.png")+`"/>`,
`<img src="`+util.URLJoin(rawwiki, "icon.png")+`"/>`)
out = render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
assert.Equal(t, `<a href="/test-owner/test-repo/media/test-branch/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/media/test-branch/LINK"/></a>`, out)
out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out)
out = render(`<img src="/LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
assert.Equal(t, `<img src="/LINK"/>`, out)
out = render(`<video src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
assert.Equal(t, `<video src="/test-owner/test-repo/LINK"></video>`, out)
out = render(`<video src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"})
assert.Equal(t, `<video src="/test-owner/test-repo/wiki/raw/LINK"></video>`, out)
out = render(`<video src="/LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
assert.Equal(t, `<video src="/LINK"></video>`, out)
}
func Test_ParseClusterFuzz(t *testing.T) {
@@ -706,5 +707,6 @@ func TestIssue18471(t *testing.T) {
func TestIsFullURL(t *testing.T) {
assert.True(t, markup.IsFullURLString("https://example.com"))
assert.True(t, markup.IsFullURLString("mailto:test@example.com"))
assert.True(t, markup.IsFullURLString("data:image/11111"))
assert.False(t, markup.IsFullURLString("/foo:bar"))
}
+1 -1
View File
@@ -67,7 +67,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
case *ast.Image:
g.transformImage(ctx, v, reader)
case *ast.Link:
g.transformLink(ctx, v, reader)
g.transformLink(ctx, v)
case *ast.List:
g.transformList(ctx, v, reader, rc)
case *ast.Text:
+15 -5
View File
@@ -542,6 +542,10 @@ func TestMathBlock(t *testing.T) {
"$$a$$",
`<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl,
},
{
"$a$ ($b$) [$c$] {$d$}",
`<p><code class="language-math is-loading">a</code> (<code class="language-math is-loading">b</code>) [$c$] {$d$}</p>` + nl,
},
}
for _, test := range testcases {
@@ -626,7 +630,7 @@ mail@domain.com
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/src/file.bin" rel="nofollow">local link</a><br/>
<a href="/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a><br/>
@@ -682,7 +686,7 @@ space</p>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="https://gitea.io/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://gitea.io/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image"/></a><br/>
@@ -740,7 +744,7 @@ space</p>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/relative/path/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/>
<a href="/relative/path/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image"/></a><br/>
@@ -857,7 +861,7 @@ space</p>
Expected: `<p>space @mention-user<br/>
/just/a/path.bin<br/>
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
@@ -975,7 +979,7 @@ space</p>
for i, c := range cases {
result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input)
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
assert.Equal(t, template.HTML(c.Expected), result, "Unexpected result in testcase %v", i)
assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
}
}
@@ -1010,4 +1014,10 @@ func TestAttention(t *testing.T) {
test(`> [!important]`, renderAttention("important", "octicon-report")+"\n</blockquote>")
test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n</blockquote>")
// escaped by mdformat
test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
// legacy GitHub style
test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
}
+8 -2
View File
@@ -31,10 +31,16 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
return nil, parser.NoChildren
}
dollars := false
var dollars bool
if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' {
dollars = true
} else if line[pos] != '\\' || line[pos+1] != '[' {
} else if line[pos] == '\\' && line[pos+1] == '[' {
if len(line[pos:]) >= 3 && line[pos+2] == '!' && bytes.Contains(line[pos:], []byte(`\]`)) {
// do not process escaped attention block: "> \[!NOTE\]"
return nil, parser.NoChildren
}
dollars = false
} else {
return nil, parser.NoChildren
}
@@ -45,6 +45,10 @@ func isPunctuation(b byte) bool {
return b == '.' || b == '!' || b == '?' || b == ',' || b == ';' || b == ':'
}
func isBracket(b byte) bool {
return b == ')'
}
func isAlphanumeric(b byte) bool {
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}
@@ -84,7 +88,7 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
break
}
suceedingCharacter := line[pos]
if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') {
if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') && !isBracket(suceedingCharacter) {
return nil
}
if line[ender-1] != '\\' {
+73 -18
View File
@@ -15,7 +15,7 @@ import (
"golang.org/x/text/language"
)
// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg
// renderAttention renders a quote marked with i.e. "> **Note**" or "> [!Warning]" with a corresponding svg
func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
n := node.(*Attention)
@@ -37,38 +37,93 @@ func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast
return ast.WalkContinue, nil
}
func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) {
// We only want attention blockquotes when the AST looks like:
// > Text("[") Text("!TYPE") Text("]")
func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
if firstParagraph.ChildCount() < 1 {
return "", nil
}
node1, ok := firstParagraph.FirstChild().(*ast.Emphasis)
if !ok {
return "", nil
}
val1 := string(node1.Text(reader.Source()))
attentionType := strings.ToLower(val1)
if g.attentionTypes.Contains(attentionType) {
return attentionType, []ast.Node{node1}
}
return "", nil
}
// grab these nodes and make sure we adhere to the attention blockquote structure
firstParagraph := v.FirstChild()
g.applyElementDir(firstParagraph)
if firstParagraph.ChildCount() < 3 {
return ast.WalkContinue, nil
func (g *ASTTransformer) extractBlockquoteAttention2(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
if firstParagraph.ChildCount() < 2 {
return "", nil
}
node1, ok := firstParagraph.FirstChild().(*ast.Text)
if !ok {
return ast.WalkContinue, nil
return "", nil
}
node2, ok := node1.NextSibling().(*ast.Text)
if !ok {
return ast.WalkContinue, nil
return "", nil
}
val1 := string(node1.Segment.Value(reader.Source()))
val2 := string(node2.Segment.Value(reader.Source()))
if strings.HasPrefix(val1, `\[!`) && val2 == `\]` {
attentionType := strings.ToLower(val1[3:])
if g.attentionTypes.Contains(attentionType) {
return attentionType, []ast.Node{node1, node2}
}
}
return "", nil
}
func (g *ASTTransformer) extractBlockquoteAttention3(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
if firstParagraph.ChildCount() < 3 {
return "", nil
}
node1, ok := firstParagraph.FirstChild().(*ast.Text)
if !ok {
return "", nil
}
node2, ok := node1.NextSibling().(*ast.Text)
if !ok {
return "", nil
}
node3, ok := node2.NextSibling().(*ast.Text)
if !ok {
return ast.WalkContinue, nil
return "", nil
}
val1 := string(node1.Segment.Value(reader.Source()))
val2 := string(node2.Segment.Value(reader.Source()))
val3 := string(node3.Segment.Value(reader.Source()))
if val1 != "[" || val3 != "]" || !strings.HasPrefix(val2, "!") {
return ast.WalkContinue, nil
return "", nil
}
// grab attention type from markdown source
attentionType := strings.ToLower(val2[1:])
if !g.attentionTypes.Contains(attentionType) {
if g.attentionTypes.Contains(attentionType) {
return attentionType, []ast.Node{node1, node2, node3}
}
return "", nil
}
func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) {
// We only want attention blockquotes when the AST looks like:
// > Text("[") Text("!TYPE") Text("]")
// > Text("\[!TYPE") TEXT("\]")
// > Text("**TYPE**")
// grab these nodes and make sure we adhere to the attention blockquote structure
firstParagraph := v.FirstChild()
g.applyElementDir(firstParagraph)
attentionType, processedNodes := g.extractBlockquoteAttentionEmphasis(firstParagraph, reader)
if attentionType == "" {
attentionType, processedNodes = g.extractBlockquoteAttention2(firstParagraph, reader)
}
if attentionType == "" {
attentionType, processedNodes = g.extractBlockquoteAttention3(firstParagraph, reader)
}
if attentionType == "" {
return ast.WalkContinue, nil
}
@@ -88,9 +143,9 @@ func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Read
attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType))
attentionParagraph.AppendChild(attentionParagraph, emphasis)
firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph)
firstParagraph.RemoveChild(firstParagraph, node1)
firstParagraph.RemoveChild(firstParagraph, node2)
firstParagraph.RemoveChild(firstParagraph, node3)
for _, processed := range processedNodes {
firstParagraph.RemoveChild(firstParagraph, processed)
}
if firstParagraph.ChildCount() == 0 {
firstParagraph.Parent().RemoveChild(firstParagraph.Parent(), firstParagraph)
}
+3 -29
View File
@@ -4,39 +4,13 @@
package markdown
import (
"path/filepath"
"code.gitea.io/gitea/modules/markup"
giteautil "code.gitea.io/gitea/modules/util"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
)
func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, reader text.Reader) {
// Links need their href to munged to be a real value
link := v.Destination
isAnchorFragment := len(link) > 0 && link[0] == '#'
if !isAnchorFragment && !markup.IsFullURLBytes(link) {
base := ctx.Links.Base
if ctx.IsWiki {
if filepath.Ext(string(link)) == "" {
// This link doesn't have a file extension - assume a regular wiki link
base = ctx.Links.WikiLink()
} else if markup.Type(string(link)) != "" {
// If it's a file type we can render, use a regular wiki link
base = ctx.Links.WikiLink()
} else {
// Otherwise, use a raw link instead
base = ctx.Links.WikiRawLink()
}
} else if ctx.Links.HasBranchInfo() {
base = ctx.Links.SrcLink()
}
link = []byte(giteautil.URLJoin(base, string(link)))
func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link) {
if link, resolved := markup.ResolveLink(ctx, string(v.Destination), "#user-content-"); resolved {
v.Destination = []byte(link)
}
if isAnchorFragment {
link = []byte("#user-content-" + string(link)[1:])
}
v.Destination = link
}
+7 -16
View File
@@ -46,7 +46,6 @@ func Init(ph *ProcessorHelper) {
DefaultProcessorHelper = *ph
}
NewSanitizer()
if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
@@ -74,7 +73,7 @@ type RenderContext struct {
Type string
IsWiki bool
Links Links
Metas map[string]string
Metas map[string]string // user, repo, mode(comment/document)
DefaultLink string
GitRepo *git.Repository
ShaExistCache map[string]bool
@@ -85,10 +84,10 @@ type RenderContext struct {
}
type Links struct {
AbsolutePrefix bool
Base string
BranchPath string
TreePath string
AbsolutePrefix bool // add absolute URL prefix to auto-resolved links like "#issue", but not for pre-provided links and medias
Base string // base prefix for pre-provided links and medias (images, videos)
BranchPath string // actually it is the ref path, eg: "branch/features/feat-12", "tag/v1.0"
TreePath string // the dir of the file, eg: "doc" if the file "doc/CHANGE.md" is being rendered
}
func (l *Links) Prefix() string {
@@ -371,22 +370,14 @@ func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error {
return ErrUnsupportedRenderExtension{extension}
}
// Type returns if markup format via the filename
func Type(filename string) string {
// DetectMarkupTypeByFileName returns the possible markup format type via the filename
func DetectMarkupTypeByFileName(filename string) string {
if parser := GetRendererByFileName(filename); parser != nil {
return parser.Name()
}
return ""
}
// IsMarkupFile reports whether file is a markup type file
func IsMarkupFile(name, markup string) bool {
if parser := GetRendererByFileName(name); parser != nil {
return parser.Name() == markup
}
return false
}
func PreviewableExtensions() []string {
extensions := make([]string, 0, len(extRenderers))
for extension := range extRenderers {
+21 -201
View File
@@ -5,13 +5,9 @@
package markup
import (
"io"
"net/url"
"regexp"
"sync"
"code.gitea.io/gitea/modules/setting"
"github.com/microcosm-cc/bluemonday"
)
@@ -21,211 +17,35 @@ type Sanitizer struct {
defaultPolicy *bluemonday.Policy
descriptionPolicy *bluemonday.Policy
rendererPolicies map[string]*bluemonday.Policy
init sync.Once
allowAllRegex *regexp.Regexp
}
var (
sanitizer = &Sanitizer{}
allowAllRegex = regexp.MustCompile(".+")
defaultSanitizer *Sanitizer
defaultSanitizerOnce sync.Once
)
// NewSanitizer initializes sanitizer with allowed attributes based on settings.
// Multiple calls to this function will only create one instance of Sanitizer during
// entire application lifecycle.
func NewSanitizer() {
sanitizer.init.Do(func() {
InitializeSanitizer()
})
}
// InitializeSanitizer (re)initializes the current sanitizer to account for changes in settings
func InitializeSanitizer() {
sanitizer.rendererPolicies = map[string]*bluemonday.Policy{}
sanitizer.defaultPolicy = createDefaultPolicy()
sanitizer.descriptionPolicy = createRepoDescriptionPolicy()
for name, renderer := range renderers {
sanitizerRules := renderer.SanitizerRules()
if len(sanitizerRules) > 0 {
policy := createDefaultPolicy()
addSanitizerRules(policy, sanitizerRules)
sanitizer.rendererPolicies[name] = policy
func GetDefaultSanitizer() *Sanitizer {
defaultSanitizerOnce.Do(func() {
defaultSanitizer = &Sanitizer{
rendererPolicies: map[string]*bluemonday.Policy{},
allowAllRegex: regexp.MustCompile(".+"),
}
}
}
func createDefaultPolicy() *bluemonday.Policy {
policy := bluemonday.UGCPolicy()
// For JS code copy and Mermaid loading state
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
// For code preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-preview-[-\w]+( file-content)?$`)).Globally()
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-num$`)).OnElements("td")
policy.AllowAttrs("data-line-number").OnElements("span")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-code chroma$`)).OnElements("td")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("div")
// For code preview (unicode escape)
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^file-view( unicode-escaped)?$`)).OnElements("table")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-escape$`)).OnElements("td")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^toggle-escape-button btn interact-bg$`)).OnElements("a") // don't use button, button might submit a form
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(ambiguous-code-point|escaped-code-point|broken-code-point)$`)).OnElements("span")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^char$`)).OnElements("span")
policy.AllowAttrs("data-tooltip-content", "data-escaped").OnElements("span")
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")
// For attention
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-header attention-\w+$`)).OnElements("blockquote")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-\w+$`)).OnElements("strong")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-icon attention-\w+ svg octicon-[\w-]+$`)).OnElements("svg")
policy.AllowAttrs("viewBox", "width", "height", "aria-hidden").OnElements("svg")
policy.AllowAttrs("fill-rule", "d").OnElements("path")
// For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
// Checkboxes
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
// Custom URL-Schemes
if len(setting.Markdown.CustomURLSchemes) > 0 {
policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
} else {
policy.AllowURLSchemesMatching(allowAllRegex)
// Even if every scheme is allowed, these three are blocked for security reasons
disallowScheme := func(*url.URL) bool {
return false
}
policy.AllowURLSchemeWithCustomPolicy("javascript", disallowScheme)
policy.AllowURLSchemeWithCustomPolicy("vbscript", disallowScheme)
policy.AllowURLSchemeWithCustomPolicy("data", disallowScheme)
}
// Allow classes for anchors
policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue( ref-external-issue)?`)).OnElements("a")
// Allow classes for task lists
policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
// Allow classes for org mode list item status.
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(unchecked|checked|indeterminate)$`)).OnElements("li")
// Allow icons
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i")
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
// Allow 'color' and 'background-color' properties for the style attribute on text elements.
policy.AllowStyles("color", "background-color").OnElements("span", "p")
// Allow generally safe attributes
generalSafeAttrs := []string{
"abbr", "accept", "accept-charset",
"accesskey", "action", "align", "alt",
"aria-describedby", "aria-hidden", "aria-label", "aria-labelledby",
"axis", "border", "cellpadding", "cellspacing", "char",
"charoff", "charset", "checked",
"clear", "cols", "colspan", "color",
"compact", "coords", "datetime", "dir",
"disabled", "enctype", "for", "frame",
"headers", "height", "hreflang",
"hspace", "ismap", "label", "lang",
"maxlength", "media", "method",
"multiple", "name", "nohref", "noshade",
"nowrap", "open", "prompt", "readonly", "rel", "rev",
"rows", "rowspan", "rules", "scope",
"selected", "shape", "size", "span",
"start", "summary", "tabindex", "target",
"title", "type", "usemap", "valign", "value",
"vspace", "width", "itemprop",
}
generalSafeElements := []string{
"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt",
"div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label",
"dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary",
"details", "caption", "figure", "figcaption",
"abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr",
}
policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...)
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
policy.AllowAttrs("itemscope", "itemtype").OnElements("div")
// FIXME: Need to handle longdesc in img but there is no easy way to do it
// Custom keyword markup
addSanitizerRules(policy, setting.ExternalSanitizerRules)
return policy
}
// createRepoDescriptionPolicy returns a minimal more strict policy that is used for
// repository descriptions.
func createRepoDescriptionPolicy() *bluemonday.Policy {
policy := bluemonday.NewPolicy()
// Allow italics and bold.
policy.AllowElements("i", "b", "em", "strong")
// Allow code.
policy.AllowElements("code")
// Allow links
policy.AllowAttrs("href", "target", "rel").OnElements("a")
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span")
policy.AllowAttrs("aria-label").OnElements("span")
return policy
}
func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
for _, rule := range rules {
if rule.AllowDataURIImages {
policy.AllowDataURIImages()
}
if rule.Element != "" {
if rule.Regexp != nil {
policy.AllowAttrs(rule.AllowAttr).Matching(rule.Regexp).OnElements(rule.Element)
} else {
policy.AllowAttrs(rule.AllowAttr).OnElements(rule.Element)
for name, renderer := range renderers {
sanitizerRules := renderer.SanitizerRules()
if len(sanitizerRules) > 0 {
policy := defaultSanitizer.createDefaultPolicy()
defaultSanitizer.addSanitizerRules(policy, sanitizerRules)
defaultSanitizer.rendererPolicies[name] = policy
}
}
}
defaultSanitizer.defaultPolicy = defaultSanitizer.createDefaultPolicy()
defaultSanitizer.descriptionPolicy = defaultSanitizer.createRepoDescriptionPolicy()
})
return defaultSanitizer
}
// SanitizeDescription sanitizes the HTML generated for a repository description.
func SanitizeDescription(s string) string {
NewSanitizer()
return sanitizer.descriptionPolicy.Sanitize(s)
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
NewSanitizer()
return sanitizer.defaultPolicy.Sanitize(s)
}
// SanitizeReader sanitizes a Reader
func SanitizeReader(r io.Reader, renderer string, w io.Writer) error {
NewSanitizer()
policy, exist := sanitizer.rendererPolicies[renderer]
if !exist {
policy = sanitizer.defaultPolicy
}
return policy.SanitizeReaderToWriter(r, w)
func ResetDefaultSanitizerForTesting() {
defaultSanitizer = nil
defaultSanitizerOnce = sync.Once{}
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"code.gitea.io/gitea/modules/setting"
"github.com/microcosm-cc/bluemonday"
)
func (st *Sanitizer) addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
for _, rule := range rules {
if rule.AllowDataURIImages {
policy.AllowDataURIImages()
}
if rule.Element != "" {
if rule.Regexp != nil {
policy.AllowAttrs(rule.AllowAttr).Matching(rule.Regexp).OnElements(rule.Element)
} else {
policy.AllowAttrs(rule.AllowAttr).OnElements(rule.Element)
}
}
}
}
+146
View File
@@ -0,0 +1,146 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"io"
"net/url"
"regexp"
"code.gitea.io/gitea/modules/setting"
"github.com/microcosm-cc/bluemonday"
)
func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
policy := bluemonday.UGCPolicy()
// For JS code copy and Mermaid loading state
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
// For code preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-preview-[-\w]+( file-content)?$`)).Globally()
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-num$`)).OnElements("td")
policy.AllowAttrs("data-line-number").OnElements("span")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-code chroma$`)).OnElements("td")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-inner$`)).OnElements("div")
// For code preview (unicode escape)
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^file-view( unicode-escaped)?$`)).OnElements("table")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^lines-escape$`)).OnElements("td")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^toggle-escape-button btn interact-bg$`)).OnElements("a") // don't use button, button might submit a form
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(ambiguous-code-point|escaped-code-point|broken-code-point)$`)).OnElements("span")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^char$`)).OnElements("span")
policy.AllowAttrs("data-tooltip-content", "data-escaped").OnElements("span")
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")
// For attention
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-header attention-\w+$`)).OnElements("blockquote")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-\w+$`)).OnElements("strong")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-icon attention-\w+ svg octicon-[\w-]+$`)).OnElements("svg")
policy.AllowAttrs("viewBox", "width", "height", "aria-hidden").OnElements("svg")
policy.AllowAttrs("fill-rule", "d").OnElements("path")
// For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
// Checkboxes
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
// Custom URL-Schemes
if len(setting.Markdown.CustomURLSchemes) > 0 {
policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
} else {
policy.AllowURLSchemesMatching(st.allowAllRegex)
// Even if every scheme is allowed, these three are blocked for security reasons
disallowScheme := func(*url.URL) bool {
return false
}
policy.AllowURLSchemeWithCustomPolicy("javascript", disallowScheme)
policy.AllowURLSchemeWithCustomPolicy("vbscript", disallowScheme)
policy.AllowURLSchemeWithCustomPolicy("data", disallowScheme)
}
// Allow classes for anchors
policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue( ref-external-issue)?`)).OnElements("a")
// Allow classes for task lists
policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
// Allow classes for org mode list item status.
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(unchecked|checked|indeterminate)$`)).OnElements("li")
// Allow icons
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i")
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
// Allow 'color' and 'background-color' properties for the style attribute on text elements.
policy.AllowStyles("color", "background-color").OnElements("span", "p")
// Allow generally safe attributes
generalSafeAttrs := []string{
"abbr", "accept", "accept-charset",
"accesskey", "action", "align", "alt",
"aria-describedby", "aria-hidden", "aria-label", "aria-labelledby",
"axis", "border", "cellpadding", "cellspacing", "char",
"charoff", "charset", "checked",
"clear", "cols", "colspan", "color",
"compact", "coords", "datetime", "dir",
"disabled", "enctype", "for", "frame",
"headers", "height", "hreflang",
"hspace", "ismap", "label", "lang",
"maxlength", "media", "method",
"multiple", "name", "nohref", "noshade",
"nowrap", "open", "prompt", "readonly", "rel", "rev",
"rows", "rowspan", "rules", "scope",
"selected", "shape", "size", "span",
"start", "summary", "tabindex", "target",
"title", "type", "usemap", "valign", "value",
"vspace", "width", "itemprop",
}
generalSafeElements := []string{
"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt",
"div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label",
"dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary",
"details", "caption", "figure", "figcaption",
"abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr",
}
policy.AllowAttrs(generalSafeAttrs...).OnElements(generalSafeElements...)
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
policy.AllowAttrs("itemscope", "itemtype").OnElements("div")
// FIXME: Need to handle longdesc in img but there is no easy way to do it
// Custom keyword markup
defaultSanitizer.addSanitizerRules(policy, setting.ExternalSanitizerRules)
return policy
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
return GetDefaultSanitizer().defaultPolicy.Sanitize(s)
}
// SanitizeReader sanitizes a Reader
func SanitizeReader(r io.Reader, renderer string, w io.Writer) error {
policy, exist := GetDefaultSanitizer().rendererPolicies[renderer]
if !exist {
policy = GetDefaultSanitizer().defaultPolicy
}
return policy.SanitizeReaderToWriter(r, w)
}
@@ -5,18 +5,16 @@
package markup
import (
"html/template"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Sanitizer(t *testing.T) {
NewSanitizer()
func TestSanitizer(t *testing.T) {
testCases := []string{
// Regular
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, `<a href="http://www.google.com" rel="nofollow">Google</a>`,
"<scrİpt>&lt;script&gt;alert(document.domain)&lt;/script&gt;</scrİpt>", "&lt;script&gt;alert(document.domain)&lt;/script&gt;",
// Code highlighting class
`<code class="random string"></code>`, `<code></code>`,
@@ -72,34 +70,3 @@ func Test_Sanitizer(t *testing.T) {
assert.Equal(t, testCases[i+1], Sanitize(testCases[i]))
}
}
func TestDescriptionSanitizer(t *testing.T) {
NewSanitizer()
testCases := []string{
`<h1>Title</h1>`, `Title`,
`<img src='img.png' alt='image'>`, ``,
`<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
`<span style="color: red">Hello World</span>`, `<span>Hello World</span>`,
`<br>`, ``,
`<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`, `<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`,
`<mark>Important!</mark>`, `Important!`,
`<details>Click me! <summary>Nothing to see here.</summary></details>`, `Click me! Nothing to see here.`,
`<input type="hidden">`, ``,
`<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`, `<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`,
`Provides alternative <code>wg(8)</code> tool`, `Provides alternative <code>wg(8)</code> tool`,
}
for i := 0; i < len(testCases); i += 2 {
assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i]))
}
}
func TestSanitizeNonEscape(t *testing.T) {
descStr := "<scrİpt>&lt;script&gt;alert(document.domain)&lt;/script&gt;</scrİpt>"
output := template.HTML(Sanitize(descStr))
if strings.Contains(string(output), "<script>") {
t.Errorf("un-escaped <script> in output: %q", output)
}
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"regexp"
"github.com/microcosm-cc/bluemonday"
)
// createRepoDescriptionPolicy returns a minimal more strict policy that is used for
// repository descriptions.
func (st *Sanitizer) createRepoDescriptionPolicy() *bluemonday.Policy {
policy := bluemonday.NewPolicy()
policy.AllowStandardURLs()
// Allow italics and bold.
policy.AllowElements("i", "b", "em", "strong")
// Allow code.
policy.AllowElements("code")
// Allow links
policy.AllowAttrs("href", "target", "rel").OnElements("a")
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span")
policy.AllowAttrs("aria-label").OnElements("span")
return policy
}
// SanitizeDescription sanitizes the HTML generated for a repository description.
func SanitizeDescription(s string) string {
return GetDefaultSanitizer().descriptionPolicy.Sanitize(s)
}
@@ -0,0 +1,31 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDescriptionSanitizer(t *testing.T) {
testCases := []string{
`<h1>Title</h1>`, `Title`,
`<img src='img.png' alt='image'>`, ``,
`<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
`<span style="color: red">Hello World</span>`, `<span>Hello World</span>`,
`<br>`, ``,
`<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`, `<a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">https://example.com</a>`,
`<a href="data:1234">data</a>`, `data`,
`<mark>Important!</mark>`, `Important!`,
`<details>Click me! <summary>Nothing to see here.</summary></details>`, `Click me! Nothing to see here.`,
`<input type="hidden">`, ``,
`<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`, `<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`,
`Provides alternative <code>wg(8)</code> tool`, `Provides alternative <code>wg(8)</code> tool`,
}
for i := 0; i < len(testCases); i += 2 {
assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i]))
}
}
+1
View File
@@ -45,6 +45,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository,
if err != nil {
return 0, fmt.Errorf("UpdateRepository: %w", err)
}
repo.ObjectFormatName = objFmt.Name() // keep consistent with db
allBranches := container.Set[string]{}
{
+2 -2
View File
@@ -326,14 +326,14 @@ func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
LogStartupProblem(1, log.ERROR, "Deprecated config option `[%s].%s` is present, please use `[%s].%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
}
}
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` present but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
}
}
-5
View File
@@ -6,7 +6,6 @@ package setting
import (
"fmt"
"math"
"net/url"
"os"
"path/filepath"
@@ -19,7 +18,6 @@ var (
Storage *Storage
Enabled bool
ChunkedUploadPath string
RegistryHost string
LimitTotalOwnerCount int64
LimitTotalOwnerSize int64
@@ -66,9 +64,6 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
return err
}
appURL, _ := url.Parse(AppURL)
Packages.RegistryHost = appURL.Host
Packages.ChunkedUploadPath = filepath.ToSlash(sec.Key("CHUNKED_UPLOAD_PATH").MustString("tmp/package-upload"))
if !filepath.IsAbs(Packages.ChunkedUploadPath) {
Packages.ChunkedUploadPath = filepath.ToSlash(filepath.Join(AppDataPath, Packages.ChunkedUploadPath))
+4 -2
View File
@@ -25,7 +25,8 @@ type MarkupOption struct {
//
// in: body
Mode string
// Context to render
// URL path for rendering issue, media and file links
// Expected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}
//
// in: body
Context string
@@ -53,7 +54,8 @@ type MarkdownOption struct {
//
// in: body
Mode string
// Context to render
// URL path for rendering issue, media and file links
// Expected format: /subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}
//
// in: body
Context string
+2 -2
View File
@@ -24,7 +24,7 @@ type Team struct {
// CreateTeamOption options for creating a team
type CreateTeamOption struct {
// required: true
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(255)"`
Description string `json:"description" binding:"MaxSize(255)"`
IncludesAllRepositories bool `json:"includes_all_repositories"`
// enum: read,write,admin
@@ -40,7 +40,7 @@ type CreateTeamOption struct {
// EditTeamOption options for editing a team
type EditTeamOption struct {
// required: true
Name string `json:"name" binding:"AlphaDashDot;MaxSize(30)"`
Name string `json:"name" binding:"AlphaDashDot;MaxSize(255)"`
Description *string `json:"description" binding:"MaxSize(255)"`
IncludesAllRepositories *bool `json:"includes_all_repositories"`
// enum: read,write,admin
+29 -2
View File
@@ -9,6 +9,7 @@ import (
"html"
"html/template"
"net/url"
"reflect"
"slices"
"strings"
"time"
@@ -237,8 +238,8 @@ func DotEscape(raw string) string {
// Iif is an "inline-if", similar util.Iif[T] but templates need the non-generic version,
// and it could be simply used as "{{Iif expr trueVal}}" (omit the falseVal).
func Iif(condition bool, vals ...any) any {
if condition {
func Iif(condition any, vals ...any) any {
if isTemplateTruthy(condition) {
return vals[0]
} else if len(vals) > 1 {
return vals[1]
@@ -246,6 +247,32 @@ func Iif(condition bool, vals ...any) any {
return nil
}
func isTemplateTruthy(v any) bool {
if v == nil {
return false
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Bool:
return rv.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() != 0
case reflect.Float32, reflect.Float64:
return rv.Float() != 0
case reflect.Complex64, reflect.Complex128:
return rv.Complex() != 0
case reflect.String, reflect.Slice, reflect.Array, reflect.Map:
return rv.Len() > 0
case reflect.Struct:
return true
default:
return !rv.IsNil()
}
}
// Eval the expression and return the result, see the comment of eval.Expr for details.
// To use this helper function in templates, pass each token as a separate parameter.
//
+41
View File
@@ -5,8 +5,11 @@ package templates
import (
"html/template"
"strings"
"testing"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -65,3 +68,41 @@ func TestHTMLFormat(t *testing.T) {
func TestSanitizeHTML(t *testing.T) {
assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`))
}
func TestTemplateTruthy(t *testing.T) {
tmpl := template.New("test")
tmpl.Funcs(template.FuncMap{"Iif": Iif})
template.Must(tmpl.Parse(`{{if .Value}}true{{else}}false{{end}}:{{Iif .Value "true" "false"}}`))
cases := []any{
nil, false, true, "", "string", 0, 1,
byte(0), byte(1), int64(0), int64(1), float64(0), float64(1),
complex(0, 0), complex(1, 0),
(chan int)(nil), make(chan int),
(func())(nil), func() {},
util.ToPointer(0), util.ToPointer(util.ToPointer(0)),
util.ToPointer(1), util.ToPointer(util.ToPointer(1)),
[0]int{},
[1]int{0},
[]int(nil),
[]int{},
[]int{0},
map[any]any(nil),
map[any]any{},
map[any]any{"k": "v"},
(*struct{})(nil),
struct{}{},
util.ToPointer(struct{}{}),
}
w := &strings.Builder{}
truthyCount := 0
for i, v := range cases {
w.Reset()
assert.NoError(t, tmpl.Execute(w, struct{ Value any }{v}), "case %d (%T) %#v fails", i, v, v)
out := w.String()
truthyCount += util.Iif(out == "true:true", 1, 0)
truthyMatches := out == "true:true" || out == "false:false"
assert.True(t, truthyMatches, "case %d (%T) %#v fail: %s", i, v, v, out)
}
assert.True(t, truthyCount != 0 && truthyCount != len(cases))
}
+2 -2
View File
@@ -174,7 +174,7 @@ func TestRenderMarkdownToHtml(t *testing.T) {
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a>
<a href="/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/src/file.bin" rel="nofollow">local link</a>
<a href="/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a>
@@ -190,7 +190,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
#123
space</p>
`
assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput()))
assert.Equal(t, expected, string(RenderMarkdownToHtml(context.Background(), testInput())))
}
func TestRenderLabels(t *testing.T) {
+4 -2
View File
@@ -34,8 +34,10 @@ func IsNormalPageCompleted(s string) bool {
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
}
func MockVariableValue[T any](p *T, v T) (reset func()) {
func MockVariableValue[T any](p *T, v ...T) (reset func()) {
old := *p
*p = v
if len(v) > 0 {
*p = v[0]
}
return func() { *p = old }
}
+4 -4
View File
@@ -35,6 +35,10 @@ func GetSiteCookie(req *http.Request, name string) string {
// SetSiteCookie returns given cookie value from request header.
func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
deleteLegacySiteCookie(resp, name)
cookie := &http.Cookie{
Name: name,
Value: url.QueryEscape(value),
@@ -46,10 +50,6 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
SameSite: setting.SessionConfig.SameSite,
}
resp.Header().Add("Set-Cookie", cookie.String())
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
deleteLegacySiteCookie(resp, name)
}
// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
+2 -2
View File
@@ -1554,9 +1554,9 @@ issues.no_content = No description provided.
issues.close = Close Issue
issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s
issues.comment_manually_pull_merged_at = manually merged commit %[1]s into %[2]s %[3]s
issues.close_comment_issue = Comment and Close
issues.close_comment_issue = Close with Comment
issues.reopen_issue = Reopen
issues.reopen_comment_issue = Comment and Reopen
issues.reopen_comment_issue = Reopen with Comment
issues.create_comment = Comment
issues.comment.blocked_user = Cannot create or edit comment because you are blocked by the poster or repository owner.
issues.closed_at = `closed this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+2 -2
View File
@@ -39,7 +39,7 @@ func saveUploadChunkBase(st storage.ObjectStorage, ctx *ArtifactContext,
r = io.TeeReader(r, hasher)
}
// save chunk to storage
writtenSize, err := st.Save(storagePath, r, -1)
writtenSize, err := st.Save(storagePath, r, contentSize)
if err != nil {
return -1, fmt.Errorf("save chunk to storage error: %v", err)
}
@@ -208,7 +208,7 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
// save merged file
storagePath := fmt.Sprintf("%d/%d/%d.%s", artifact.RunID%255, artifact.ID%255, time.Now().UnixNano(), extension)
written, err := st.Save(storagePath, mergedReader, -1)
written, err := st.Save(storagePath, mergedReader, artifact.FileCompressedSize)
if err != nil {
return fmt.Errorf("save merged file error: %v", err)
}
+3 -3
View File
@@ -116,9 +116,9 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
}
func apiUnauthorizedError(ctx *context.Context) {
// TODO: it doesn't seem quite right but it doesn't really cause problem at the moment.
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed, ideally.
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+httplib.GuessCurrentAppURL(ctx)+`v2/token",service="container_registry",scope="*"`)
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed
realmURL := httplib.GuessCurrentHostURL(ctx) + "/v2/token"
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+realmURL+`",service="container_registry",scope="*"`)
apiErrorDefined(ctx, errUnauthorized)
}
+29 -19
View File
@@ -96,20 +96,34 @@ func FeedCapabilityResource(ctx *context.Context) {
xmlResponse(ctx, http.StatusOK, Metadata)
}
var searchTermExtract = regexp.MustCompile(`'([^']+)'`)
var (
searchTermExtract = regexp.MustCompile(`'([^']+)'`)
searchTermExact = regexp.MustCompile(`\s+eq\s+'`)
)
func getSearchTerm(ctx *context.Context) string {
func getSearchTerm(ctx *context.Context) packages_model.SearchValue {
searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'")
if searchTerm == "" {
// $filter contains a query like:
// (((Id ne null) and substringof('microsoft',tolower(Id)))
// We don't support these queries, just extract the search term.
match := searchTermExtract.FindStringSubmatch(ctx.FormTrim("$filter"))
if len(match) == 2 {
searchTerm = strings.TrimSpace(match[1])
if searchTerm != "" {
return packages_model.SearchValue{
Value: searchTerm,
ExactMatch: false,
}
}
return searchTerm
// $filter contains a query like:
// (((Id ne null) and substringof('microsoft',tolower(Id)))
// https://www.odata.org/documentation/odata-version-2-0/uri-conventions/ section 4.5
// We don't support these queries, just extract the search term.
filter := ctx.FormTrim("$filter")
match := searchTermExtract.FindStringSubmatch(filter)
if len(match) == 2 {
return packages_model.SearchValue{
Value: strings.TrimSpace(match[1]),
ExactMatch: searchTermExact.MatchString(filter),
}
}
return packages_model.SearchValue{}
}
// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
@@ -118,11 +132,9 @@ func SearchServiceV2(ctx *context.Context) {
paginator := db.NewAbsoluteListOptions(skip, take)
pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Type: packages_model.TypeNuGet,
Name: packages_model.SearchValue{
Value: getSearchTerm(ctx),
},
OwnerID: ctx.Package.Owner.ID,
Type: packages_model.TypeNuGet,
Name: getSearchTerm(ctx),
IsInternal: optional.Some(false),
Paginator: paginator,
})
@@ -169,10 +181,8 @@ func SearchServiceV2(ctx *context.Context) {
// http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html#_Toc453752351
func SearchServiceV2Count(ctx *context.Context) {
count, err := nuget_model.CountPackages(ctx, &packages_model.PackageSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Name: packages_model.SearchValue{
Value: getSearchTerm(ctx),
},
OwnerID: ctx.Package.Owner.ID,
Name: getSearchTerm(ctx),
IsInternal: optional.Some(false),
})
if err != nil {
+61 -39
View File
@@ -7,6 +7,7 @@ import (
go_context "context"
"io"
"net/http"
"path"
"strings"
"testing"
@@ -19,36 +20,40 @@ import (
"github.com/stretchr/testify/assert"
)
const (
AppURL = "http://localhost:3000/"
Repo = "gogits/gogs"
FullURL = AppURL + Repo + "/"
)
const AppURL = "http://localhost:3000/"
func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expectedBody string, expectedCode int) {
setting.AppURL = AppURL
context := "/gogits/gogs"
if !wiki {
context += path.Join("/src/branch/main", path.Dir(filePath))
}
options := api.MarkupOption{
Mode: mode,
Text: text,
Context: Repo,
Wiki: true,
Context: context,
Wiki: wiki,
FilePath: filePath,
}
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markup")
web.SetForm(ctx, &options)
Markup(ctx)
assert.Equal(t, responseBody, resp.Body.String())
assert.Equal(t, responseCode, resp.Code)
assert.Equal(t, expectedBody, resp.Body.String())
assert.Equal(t, expectedCode, resp.Code)
resp.Body.Reset()
}
func testRenderMarkdown(t *testing.T, mode, text, responseBody string, responseCode int) {
func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody string, responseCode int) {
setting.AppURL = AppURL
context := "/gogits/gogs"
if !wiki {
context += "/src/branch/main"
}
options := api.MarkdownOption{
Mode: mode,
Text: text,
Context: Repo,
Wiki: true,
Context: context,
Wiki: wiki,
}
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
web.SetForm(ctx, &options)
@@ -65,7 +70,7 @@ func TestAPI_RenderGFM(t *testing.T) {
},
})
testCasesCommon := []string{
testCasesWiki := []string{
// dear imgui wiki markdown extract: special wiki syntax
`Wiki! Enjoy :)
- [[Links, Language bindings, Engine bindings|Links]]
@@ -74,20 +79,20 @@ func TestAPI_RenderGFM(t *testing.T) {
// rendered
`<p>Wiki! Enjoy :)</p>
<ul>
<li><a href="` + FullURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
<li><a href="` + FullURL + `wiki/Tips" rel="nofollow">Tips</a></li>
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">https://github.com/ocornut/imgui/issues/786</a></li>
<li><a href="http://localhost:3000/gogits/gogs/wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
<li><a href="http://localhost:3000/gogits/gogs/wiki/Tips" rel="nofollow">Tips</a></li>
<li>Bezier widget (by <a href="http://localhost:3000/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">https://github.com/ocornut/imgui/issues/786</a></li>
</ul>
`,
// Guard wiki sidebar: special syntax
`[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`,
// rendered
`<p><a href="` + FullURL + `wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p>
`<p><a href="http://localhost:3000/gogits/gogs/wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p>
`,
// special syntax
`[[Name|Link]]`,
// rendered
`<p><a href="` + FullURL + `wiki/Link" rel="nofollow">Name</a></p>
`<p><a href="http://localhost:3000/gogits/gogs/wiki/Link" rel="nofollow">Name</a></p>
`,
// empty
``,
@@ -95,7 +100,7 @@ func TestAPI_RenderGFM(t *testing.T) {
``,
}
testCasesDocument := []string{
testCasesWikiDocument := []string{
// wine-staging wiki home extract: special wiki syntax, images
`## What is Wine Staging?
**Wine Staging** on website [wine-staging.com](http://wine-staging.com).
@@ -111,31 +116,48 @@ Here are some links to the most important topics. You can find the full list of
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
<h2 id="user-content-quick-links">Quick Links</h2>
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
<p><a href="` + FullURL + `wiki/Configuration" rel="nofollow">Configuration</a>
<a href="` + FullURL + `wiki/raw/images/icon-bug.png" rel="nofollow"><img src="` + FullURL + `wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
<p><a href="http://localhost:3000/gogits/gogs/wiki/Configuration" rel="nofollow">Configuration</a>
<a href="http://localhost:3000/gogits/gogs/wiki/raw/images/icon-bug.png" rel="nofollow"><img src="http://localhost:3000/gogits/gogs/wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
`,
}
for i := 0; i < len(testCasesCommon); i += 2 {
text := testCasesCommon[i]
response := testCasesCommon[i+1]
testRenderMarkdown(t, "gfm", text, response, http.StatusOK)
testRenderMarkup(t, "gfm", "", text, response, http.StatusOK)
testRenderMarkdown(t, "comment", text, response, http.StatusOK)
testRenderMarkup(t, "comment", "", text, response, http.StatusOK)
testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK)
for i := 0; i < len(testCasesWiki); i += 2 {
text := testCasesWiki[i]
response := testCasesWiki[i+1]
testRenderMarkdown(t, "gfm", true, text, response, http.StatusOK)
testRenderMarkup(t, "gfm", true, "", text, response, http.StatusOK)
testRenderMarkdown(t, "comment", true, text, response, http.StatusOK)
testRenderMarkup(t, "comment", true, "", text, response, http.StatusOK)
testRenderMarkup(t, "file", true, "path/test.md", text, response, http.StatusOK)
}
for i := 0; i < len(testCasesDocument); i += 2 {
text := testCasesDocument[i]
response := testCasesDocument[i+1]
testRenderMarkdown(t, "gfm", text, response, http.StatusOK)
testRenderMarkup(t, "gfm", "", text, response, http.StatusOK)
testRenderMarkup(t, "file", "path/test.md", text, response, http.StatusOK)
for i := 0; i < len(testCasesWikiDocument); i += 2 {
text := testCasesWikiDocument[i]
response := testCasesWikiDocument[i+1]
testRenderMarkdown(t, "gfm", true, text, response, http.StatusOK)
testRenderMarkup(t, "gfm", true, "", text, response, http.StatusOK)
testRenderMarkup(t, "file", true, "path/test.md", text, response, http.StatusOK)
}
testRenderMarkup(t, "file", "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity)
testRenderMarkup(t, "unknown", "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity)
input := "[Link](test.md)\n![Image](image.png)"
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "gfm", false, "", input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/test.md" rel="nofollow">Link</a>
<a href="http://localhost:3000/gogits/gogs/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "file", false, "path/new-file.md", input, `<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/path/test.md" rel="nofollow">Link</a>
<a href="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "file", true, "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity)
testRenderMarkup(t, "unknown", true, "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity)
}
var simpleCases = []string{
@@ -160,7 +182,7 @@ func TestAPI_RenderSimple(t *testing.T) {
options := api.MarkdownOption{
Mode: "markdown",
Text: "",
Context: Repo,
Context: "/gogits/gogs",
}
ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown")
for i := 0; i < len(simpleCases); i += 2 {
+6
View File
@@ -319,6 +319,12 @@ func archiveDownload(ctx *context.APIContext) {
func download(ctx *context.APIContext, archiveName string, archiver *repo_model.RepoArchiver) {
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
// Add nix format link header so tarballs lock correctly:
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.tar.gz?rev=%s>; rel="immutable"`,
ctx.Repo.Repository.APIURL(),
archiver.CommitID, archiver.CommitID))
rPath := archiver.RelativePath()
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
// If we have a signed url (S3, object storage), redirect to this directly.
+1
View File
@@ -383,6 +383,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil {
if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil {
ctx.ServerError("DeletePushMirrors", err)
return
}
ctx.ServerError("AddPushMirrorRemote", err)
return
+31 -30
View File
@@ -7,62 +7,66 @@ package common
import (
"fmt"
"net/http"
"path"
"strings"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
"mvdan.cc/xurls/v2"
)
// RenderMarkup renders markup text for the /markup and /markdown endpoints
func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) {
var markupType string
relativePath := ""
func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPathContext, filePath string, wiki bool) {
// urlPathContext format is "/subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}"
// filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file")
// filePath will be used as RenderContext.RelativePath
if len(text) == 0 {
_, _ = ctx.Write([]byte(""))
return
// for example, when previewing file "/gitea/owner/repo/src/branch/features/feat-123/doc/CHANGE.md", then filePath is "doc/CHANGE.md"
// and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc"
var markupType, relativePath string
links := markup.Links{AbsolutePrefix: true}
if urlPathContext != "" {
links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
}
switch mode {
case "markdown":
// Raw markdown
if err := markdown.RenderRaw(&markup.RenderContext{
Ctx: ctx,
Links: markup.Links{
AbsolutePrefix: true,
Base: urlPrefix,
},
Ctx: ctx,
Links: links,
}, strings.NewReader(text), ctx.Resp); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
}
return
case "comment":
// Comment as markdown
// Issue & comment content
markupType = markdown.MarkupName
case "gfm":
// Github Flavored Markdown as document
// GitHub Flavored Markdown
markupType = markdown.MarkupName
case "file":
// File as document based on file extension
markupType = ""
markupType = "" // render the repo file content by its extension
relativePath = filePath
default:
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
return
}
if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) {
// check if urlPrefix is already set to a URL
linkRegex, _ := xurls.StrictMatchingScheme("https?://")
m := linkRegex.FindStringIndex(urlPrefix)
if m == nil {
urlPrefix = util.URLJoin(setting.AppURL, urlPrefix)
}
fields := strings.SplitN(strings.TrimPrefix(urlPathContext, setting.AppSubURL+"/"), "/", 5)
if len(fields) == 5 && fields[2] == "src" && (fields[3] == "branch" || fields[3] == "commit" || fields[3] == "tag") {
// absolute base prefix is something like "https://host/subpath/{user}/{repo}"
absoluteBasePrefix := fmt.Sprintf("%s%s/%s", httplib.GuessCurrentAppURL(ctx), fields[0], fields[1])
fileDir := path.Dir(filePath) // it is "doc" if filePath is "doc/CHANGE.md"
refPath := strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc"
refPath = strings.TrimSuffix(refPath, "/"+fileDir) // now we get the correct branch path: "branch/features/feat-12"
links = markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir}
}
meta := map[string]string{}
@@ -78,11 +82,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
}
if err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Links: markup.Links{
AbsolutePrefix: true,
Base: urlPrefix,
},
Ctx: ctx,
Links: links,
Metas: meta,
IsWiki: wiki,
Type: markupType,
+1
View File
@@ -95,6 +95,7 @@ func UnadoptedRepos(ctx *context.Context) {
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, q, &opts)
if err != nil {
ctx.ServerError("ListUnadoptedRepositories", err)
return
}
ctx.Data["Dirs"] = repoNames
pager := context.NewPagination(count, opts.PageSize, opts.Page, 5)
+1
View File
@@ -831,6 +831,7 @@ func ActivateEmail(ctx *context.Context) {
if email := user_model.VerifyActiveEmailCode(ctx, code, emailStr); email != nil {
if err := user_model.ActivateEmail(ctx, email); err != nil {
ctx.ServerError("ActivateEmail", err)
return
}
log.Trace("Email activated: %s", email.Email)
+1
View File
@@ -571,6 +571,7 @@ func MoveIssues(ctx *context.Context) {
form := &movedIssuesForm{}
if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.ServerError("DecodeMovedIssuesForm", err)
return
}
issueIDs := make([]int64, 0, len(form.Issues))
-6
View File
@@ -19,14 +19,8 @@ const (
// Contributors render the page to show repository contributors graph
func Contributors(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.contributors")
ctx.Data["PageIsActivity"] = true
ctx.Data["PageIsContributors"] = true
ctx.PageData["contributionType"] = "commits"
ctx.PageData["repoLink"] = ctx.Repo.RepoLink
ctx.HTML(http.StatusOK, tplContributors)
}
+1
View File
@@ -562,6 +562,7 @@ func DeleteFilePost(ctx *context.Context) {
} else {
ctx.ServerError("DeleteRepoFile", err)
}
return
}
ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath))
+8 -7
View File
@@ -1791,6 +1791,7 @@ func ViewIssue(ctx *context.Context) {
pull.Issue = issue
canDelete := false
allowMerge := false
canWriteToHeadRepo := false
if ctx.IsSigned {
if err := pull.LoadHeadRepo(ctx); err != nil {
@@ -1811,7 +1812,7 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
}
}
ctx.Data["CanWriteToHeadRepo"] = true
canWriteToHeadRepo = true
}
}
@@ -1823,6 +1824,9 @@ func ViewIssue(ctx *context.Context) {
ctx.ServerError("GetUserRepoPermission", err)
return
}
if !canWriteToHeadRepo { // maintainers maybe allowed to push to head repo even if they can't write to it
canWriteToHeadRepo = pull.AllowMaintainerEdit && perm.CanWrite(unit.TypeCode)
}
allowMerge, err = pull_service.IsUserAllowedToMerge(ctx, pull, perm, ctx.Doer)
if err != nil {
ctx.ServerError("IsUserAllowedToMerge", err)
@@ -1835,6 +1839,8 @@ func ViewIssue(ctx *context.Context) {
}
}
ctx.Data["CanWriteToHeadRepo"] = canWriteToHeadRepo
ctx.Data["ShowMergeInstructions"] = canWriteToHeadRepo
ctx.Data["AllowMerge"] = allowMerge
prUnit, err := repo.GetUnit(ctx, unit.TypePullRequests)
@@ -1889,13 +1895,9 @@ func ViewIssue(ctx *context.Context) {
ctx.ServerError("LoadProtectedBranch", err)
return
}
ctx.Data["ShowMergeInstructions"] = true
if pb != nil {
pb.Repo = pull.BaseRepo
var showMergeInstructions bool
if ctx.Doer != nil {
showMergeInstructions = pb.CanUserPush(ctx, ctx.Doer)
}
ctx.Data["ProtectedBranch"] = pb
ctx.Data["IsBlockedByApprovals"] = !issues_model.HasEnoughApprovals(ctx, pb, pull)
ctx.Data["IsBlockedByRejection"] = issues_model.MergeBlockedByRejectedReview(ctx, pb, pull)
@@ -1906,7 +1908,6 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["ChangedProtectedFiles"] = pull.ChangedProtectedFiles
ctx.Data["IsBlockedByChangedProtectedFiles"] = len(pull.ChangedProtectedFiles) != 0
ctx.Data["ChangedProtectedFilesNum"] = len(pull.ChangedProtectedFiles)
ctx.Data["ShowMergeInstructions"] = showMergeInstructions
}
ctx.Data["WillSign"] = false
if ctx.Doer != nil {
+1 -1
View File
@@ -47,7 +47,7 @@ func RenderFile(ctx *context.Context) {
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
if markupType := markup.Type(blob.Name()); markupType == "" {
if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType == "" {
if isTextFile {
_, _ = io.Copy(ctx.Resp, rd)
} else {
+9 -2
View File
@@ -418,8 +418,9 @@ func RedirectDownload(ctx *context.Context) {
tagNames := []string{vTag}
curRepo := ctx.Repo.Repository
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
RepoID: curRepo.ID,
TagNames: tagNames,
IncludeDrafts: ctx.Repo.CanWrite(unit.TypeReleases),
RepoID: curRepo.ID,
TagNames: tagNames,
})
if err != nil {
ctx.ServerError("RedirectDownload", err)
@@ -484,6 +485,12 @@ func Download(ctx *context.Context) {
func download(ctx *context.Context, archiveName string, archiver *repo_model.RepoArchiver) {
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
// Add nix format link header so tarballs lock correctly:
// https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md
ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.tar.gz?rev=%s>; rel="immutable"`,
ctx.Repo.Repository.APIURL(),
archiver.CommitID, archiver.CommitID))
rPath := archiver.RelativePath()
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
// If we have a signed url (S3, object storage), redirect to this directly.
+6 -7
View File
@@ -307,7 +307,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
if markupType := markup.Type(readmeFile.Name()); markupType != "" {
if markupType := markup.DetectMarkupTypeByFileName(readmeFile.Name()); markupType != "" {
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
@@ -499,7 +499,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
readmeExist := util.IsReadmeFileName(blob.Name())
ctx.Data["ReadmeExist"] = readmeExist
markupType := markup.Type(blob.Name())
markupType := markup.DetectMarkupTypeByFileName(blob.Name())
// If the markup is detected by custom markup renderer it should not be reset later on
// to not pass it down to the render context.
detected := false
@@ -606,9 +606,9 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
break
}
// TODO: this logic seems strange, it duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go"
// maybe for this case, the file is a binary file, and shouldn't be rendered?
if markupType := markup.Type(blob.Name()); markupType != "" {
// TODO: this logic duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go"
// It is used by "external renders", markupRender will execute external programs to get rendered content.
if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType != "" {
rd := io.MultiReader(bytes.NewReader(buf), dataRc)
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
@@ -1047,8 +1047,7 @@ func renderHomeCode(ctx *context.Context) {
baseRepoPerm.CanRead(unit_model.TypePullRequests) {
ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
if err != nil {
ctx.ServerError("FindRecentlyPushedNewBranches", err)
return
log.Error("FindRecentlyPushedNewBranches failed: %v", err)
}
}
}
+1 -1
View File
@@ -532,7 +532,7 @@ func Wiki(ctx *context.Context) {
}
wikiPath := entry.Name()
if markup.Type(wikiPath) != markdown.MarkupName {
if markup.DetectMarkupTypeByFileName(wikiPath) != markdown.MarkupName {
ext := strings.ToUpper(filepath.Ext(wikiPath))
ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
}
+7 -1
View File
@@ -5,6 +5,7 @@ package user
import (
"net/http"
"net/url"
"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
@@ -15,6 +16,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
@@ -178,7 +180,11 @@ func ViewPackageVersion(ctx *context.Context) {
switch pd.Package.Type {
case packages_model.TypeContainer:
ctx.Data["RegistryHost"] = setting.Packages.RegistryHost
registryAppURL, err := url.Parse(httplib.GuessCurrentAppURL(ctx))
if err != nil {
registryAppURL, _ = url.Parse(setting.AppURL)
}
ctx.Data["RegistryHost"] = registryAppURL.Host
case packages_model.TypeAlpine:
branches := make(container.Set[string])
repositories := make(container.Set[string])
+3 -2
View File
@@ -1125,6 +1125,9 @@ func registerRoutes(m *web.Route) {
// user/org home, including rss feeds
m.Get("/{username}/{reponame}", ignSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
// TODO: maybe it should relax the permission to allow "any access"
m.Post("/{username}/{reponame}/markup", ignSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup)
m.Group("/{username}/{reponame}", func() {
m.Get("/find/*", repo.FindFiles)
m.Group("/tree-list", func() {
@@ -1236,8 +1239,6 @@ func registerRoutes(m *web.Route) {
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
}, context.RepoMustNotBeArchived())
m.Post("/markup", web.Bind(structs.MarkupOption{}), misc.Markup)
m.Group("/labels", func() {
m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
+1
View File
@@ -236,6 +236,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
MirrorInterval: mirrorInterval,
MirrorUpdated: mirrorUpdated,
RepoTransfer: transfer,
ObjectFormatName: repo.ObjectFormatName,
}
}
+6
View File
@@ -5,6 +5,7 @@ package externalaccount
import (
"context"
"strconv"
"strings"
"code.gitea.io/gitea/models/auth"
@@ -82,6 +83,11 @@ func UpdateExternalUser(ctx context.Context, user *user_model.User, gothUser got
// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
func UpdateMigrationsByType(ctx context.Context, tp structs.GitServiceType, externalUserID string, userID int64) error {
// Skip update if externalUserID is not a valid numeric ID or exceeds int64
if _, err := strconv.ParseInt(externalUserID, 10, 64); err != nil {
return nil
}
if err := issues_model.UpdateIssuesMigrationsByType(ctx, tp, externalUserID, userID); err != nil {
return err
}

Some files were not shown because too many files have changed in this diff Show More