Кодревью изменений до 29 июня 2022 #43

Closed
Bezborodov wants to merge 54 commits from dev_mirocod into release/v1.16
  1. 130
      README.md
  2. 7
      custom/conf/app.example.ini
  3. 2
      integrations/api_repo_edit_test.go
  4. 85
      models/error.go
  5. 52
      models/issue.go
  6. 52
      models/issue_comment.go
  7. 67
      models/issue_comment_list.go
  8. 134
      models/issue_parent.go
  9. 61
      models/issue_parent_test.go
  10. 3
      models/migrations/v70.go
  11. 4
      models/org_team.go
  12. 5
      models/repo.go
  13. 16
      models/repo/issue.go
  14. 1
      models/repo/repo_unit.go
  15. 24
      models/user/user.go
  16. 5
      modules/context/repo.go
  17. 1
      modules/convert/convert.go
  18. 10
      modules/convert/issue_comment.go
  19. 1
      modules/convert/repository.go
  20. 2
      modules/convert/user.go
  21. 4
      modules/doctor/fix16961.go
  22. 4
      modules/doctor/fix16961_test.go
  23. 4
      modules/setting/service.go
  24. 1
      modules/structs/admin_user.go
  25. 2
      modules/structs/issue_comment.go
  26. 7
      modules/structs/org.go
  27. 4
      modules/structs/org_team.go
  28. 10
      modules/structs/repo.go
  29. 6
      modules/structs/user.go
  30. 571
      options/locale/locale_ru-RU.ini
  31. 45
      package-lock.json
  32. BIN
      public/img/logo.png
  33. 108
      public/img/logo.svg
  34. 102
      public/img/logo1.svg
  35. 51
      public/img/logo2.svg
  36. 1
      routers/api/v1/admin/org.go
  37. 3
      routers/api/v1/admin/user.go
  38. 2
      routers/api/v1/org/org.go
  39. 2
      routers/api/v1/repo/repo.go
  40. 3
      routers/api/v1/user/settings.go
  41. 2
      routers/web/admin/users.go
  42. 127
      routers/web/map/umap.go
  43. 1
      routers/web/org/setting.go
  44. 78
      routers/web/repo/issue.go
  45. 129
      routers/web/repo/issue_parent.go
  46. 2
      routers/web/repo/milestone.go
  47. 1
      routers/web/repo/setting.go
  48. 10
      routers/web/repo/view.go
  49. 1
      routers/web/user/setting/profile.go
  50. 18
      routers/web/web.go
  51. 2
      services/forms/admin.go
  52. 3
      services/forms/org.go
  53. 3
      services/forms/repo_form.go
  54. 5
      services/forms/user_form.go
  55. 2
      templates/admin/config.tmpl
  56. 8
      templates/admin/user/edit.tmpl
  57. 1
      templates/base/head_navbar.tmpl
  58. 2
      templates/explore/organizations.tmpl
  59. 2
      templates/explore/users.tmpl
  60. 18
      templates/map/navbar.tmpl
  61. 2862
      templates/map/umap.tmpl
  62. 2
      templates/org/home.tmpl
  63. 5
      templates/org/settings/options.tmpl
  64. 43
      templates/repo/header.tmpl
  65. 89
      templates/repo/issue/issue_actions.tmpl
  66. 90
      templates/repo/issue/issue_filters.tmpl
  67. 204
      templates/repo/issue/list.tmpl
  68. 23
      templates/repo/issue/nav_search_and_new.tmpl
  69. 1
      templates/repo/issue/navbar.tmpl
  70. 20
      templates/repo/issue/treant_config.tmpl
  71. 84
      templates/repo/issue/tree.tmpl
  72. 50
      templates/repo/issue/view_content/comments.tmpl
  73. 125
      templates/repo/issue/view_content/sidebar.tmpl
  74. 6
      templates/repo/settings/options.tmpl
  75. 2
      templates/repo/user_cards.tmpl
  76. 144
      templates/shared/issuelistfortree.tmpl
  77. 5
      templates/swagger/v1_json.tmpl
  78. 2
      templates/user/profile.tmpl
  79. 4
      templates/user/settings/profile.tmpl
  80. 46
      web_src/js/features/repo-issue.js
  81. 3
      web_src/js/features/repo-legacy.js
  82. 15
      web_src/less/_repository.less

130
README.md

@ -1,11 +1,13 @@
<p align="center"> <p align="center">
<a href="https://gitea.io/"> <a href="http://git.mirocod.ru/MIROCOD/Platform_Mirocod/">
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/> <img alt="Мирокод" src="http://git.mirocod.ru/MIROCOD/Platform_Mirocod/raw/branch/dev_mirocod/public/img/logo.svg" width="417"/>
</a> </a>
</p> </p>
<h1 align="center">Gitea - Git with a cup of tea</h1> <h1 align="center">Платформа ЦРНП "Мирокод" для разработки проектов</h1>
<!--
<p align="center"> <p align="center">
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status"> <a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main"> <img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
</a> </a>
@ -47,114 +49,28 @@
<p align="center"> <p align="center">
<a href="README_ZH.md">View the chinese version of this document</a> <a href="README_ZH.md">View the chinese version of this document</a>
</p> </p>
-->
## Purpose ## Общее описание
The goal of this project is to make the easiest, fastest, and most Платформа "Мирокод" - необходимый инструмент для разработки народных проектов.
painless way of setting up a self-hosted Git service.
Using Go, this can be done with an independent binary distribution across Разрабатывается на основе gitea, в перспективе планируется реализовать:
**all platforms** which Go supports, including Linux, macOS, and Windows 1. Карта пользователей (их ресурсы, компетенции и интересы), проектов, сообществ.
on x86, amd64, ARM and PowerPC architectures. 2. Дерево задач в каждом проекте
Want to try it before doing anything else? 3. Дерево проектов
Do it [with the online demo](https://try.gitea.io/)! 4. Проекты могут быть не только для разработки, но и любые общественные проекты
This project has been 5. У пользователя можно будет указать ресурсы, компетенции, интересы
6. Объединяются в одном месте, люди, проекты, ресурсы и компетенции, а также инструменты ведения проектов и решения задач.
7. В перспективе самый простой модуль игроподобия, для того чтобы легко подбирать новые задачи из любых проектов
8. Метрика по проектам
Проект создан на основе:
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from [forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
[Gogs](https://gogs.io) since 2016.11 but changed a lot. [Gogs](https://gogs.io) since 2016.11 but changed a lot.
## Building Подробное описание сборки и пр. - http://git.mirocod.ru/MIROCOD/Platform_Mirocod/wiki
From the root of the source tree, run:
TAGS="bindata" make build
or if SQLite support is required:
TAGS="bindata sqlite sqlite_unlock_notify" make build
The `build` target is split into two sub-targets:
- `make backend` which requires [Go 1.17](https://go.dev/dl/) or greater.
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
Parallelism (`make -j <num>`) is not supported.
More info: https://docs.gitea.io/en-us/install-from-source/
## Using
./gitea web
NOTE: If you're interested in using our APIs, we have experimental
support with [documentation](https://try.gitea.io/api/swagger).
## Contributing
Expected workflow is: Fork -> Patch -> Push -> Pull Request
NOTES:
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
## Translating
Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope fo fill it as questions pop up.
https://docs.gitea.io/en-us/translation-guidelines/
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
## Further information
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.io/en-us/).
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/).
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
The hugo-based documentation theme is hosted at [gitea/theme](https://gitea.com/gitea/theme).
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
## Authors
* [Maintainers](https://github.com/orgs/go-gitea/people)
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
* [Translators](options/locale/TRANSLATORS)
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## FAQ
**How do you pronounce Gitea?**
Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g.
**Why is this not hosted on a Gitea instance?**
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
## License ## Лицензия
This project is licensed under the MIT License. This project is licensed under the MIT License.
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file

7
custom/conf/app.example.ini

@ -701,6 +701,13 @@ PATH =
;; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting. ;; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
;ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true ;ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true
;; ;;
;; Default value for EnableParents
;; Repositories will use parents by default depending on this setting
;DEFAULT_ENABLE_PARENTS = true
;;
;; Parents can be added from any repository where the user is granted access or only from the current repository depending on this setting.
;ALLOW_CROSS_REPOSITORY_PARENTS = true
;;
;; Enable heatmap on users profiles. ;; Enable heatmap on users profiles.
;ENABLE_USER_HEATMAP = true ;ENABLE_USER_HEATMAP = true
;; ;;

2
integrations/api_repo_edit_test.go

@ -35,6 +35,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption
EnableTimeTracker: config.EnableTimetracker, EnableTimeTracker: config.EnableTimetracker,
AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime,
EnableIssueDependencies: config.EnableDependencies, EnableIssueDependencies: config.EnableDependencies,
EnableIssueParents: config.EnableParents,
} }
} else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil { } else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil {
config := unit.ExternalTrackerConfig() config := unit.ExternalTrackerConfig()
@ -182,6 +183,7 @@ func TestAPIRepoEdit(t *testing.T) {
EnableTimeTracker: false, EnableTimeTracker: false,
AllowOnlyContributorsToTrackTime: false, AllowOnlyContributorsToTrackTime: false,
EnableIssueDependencies: false, EnableIssueDependencies: false,
EnableIssueParents: false,
} }
*repoEditOption.HasWiki = true *repoEditOption.HasWiki = true
repoEditOption.ExternalWiki = nil repoEditOption.ExternalWiki = nil

85
models/error.go

@ -1340,6 +1340,91 @@ func (err ErrUnknownDependencyType) Error() string {
return fmt.Sprintf("unknown dependency type [type: %d]", err.Type) return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
} }
// .___ ________ .___ .__
// | | ______ ________ __ ____ \______ \ ____ ______ ____ ____ __| _/____ ____ ____ |__| ____ ______
// | |/ ___// ___/ | \_/ __ \ | | \_/ __ \\____ \_/ __ \ / \ / __ |/ __ \ / \_/ ___\| |/ __ \ / ___/
// | |\___ \ \___ \| | /\ ___/ | ` \ ___/| |_> > ___/| | \/ /_/ \ ___/| | \ \___| \ ___/ \___ \
// |___/____ >____ >____/ \___ >_______ /\___ > __/ \___ >___| /\____ |\___ >___| /\___ >__|\___ >____ >
// \/ \/ \/ \/ \/|__| \/ \/ \/ \/ \/ \/ \/ \/
// ErrParentExists represents a "ParentAlreadyExists" kind of error.
type ErrParentExists struct {
IssueID int64
ParentID int64
}
// IsErrParentExists checks if an error is a ErrParentExists.
func IsErrParentExists(err error) bool {
_, ok := err.(ErrParentExists)
return ok
}
func (err ErrParentExists) Error() string {
return fmt.Sprintf("issue parent does already exist [issue id: %d, parent id: %d]", err.IssueID, err.ParentID)
}
// ErrParentNotExists represents a "ParentAlreadyExists" kind of error.
type ErrParentNotExists struct {
IssueID int64
ParentID int64
}
// IsErrParentNotExists checks if an error is a ErrParentExists.
func IsErrParentNotExists(err error) bool {
_, ok := err.(ErrParentNotExists)
return ok
}
func (err ErrParentNotExists) Error() string {
return fmt.Sprintf("issue parent does not exist [issue id: %d, parent id: %d]", err.IssueID, err.ParentID)
}
// ErrCircularParent represents a "ParentCircular" kind of error.
type ErrCircularParent struct {
IssueID int64
ParentID int64
}
// IsErrCircularParent checks if an error is a ErrCircularParent.
func IsErrCircularParent(err error) bool {
_, ok := err.(ErrCircularParent)
return ok
}
func (err ErrCircularParent) Error() string {
return fmt.Sprintf("circular parents exists (two issues blocking each other) [issue id: %d, parent id: %d]", err.IssueID, err.ParentID)
}
// ErrParentsLeft represents an error where the issue you're trying to close still has parents left.
type ErrParentsLeft struct {
IssueID int64
}
// IsErrParentsLeft checks if an error is a ErrParentsLeft.
func IsErrParentsLeft(err error) bool {
_, ok := err.(ErrParentsLeft)
return ok
}
func (err ErrParentsLeft) Error() string {
return fmt.Sprintf("issue has open parents [issue id: %d]", err.IssueID)
}
// ErrUnknownParentType represents an error where an unknown parent type was passed
type ErrUnknownParentType struct {
Type ParentType
}
// IsErrUnknownParentType checks if an error is ErrUnknownParentType
func IsErrUnknownParentType(err error) bool {
_, ok := err.(ErrUnknownParentType)
return ok
}
func (err ErrUnknownParentType) Error() string {
return fmt.Sprintf("unknown parent type [type: %d]", err.Type)
}
// __________ .__ // __________ .__
// \______ \ _______ _|__| ______ _ __ // \______ \ _______ _|__| ______ _ __
// | _// __ \ \/ / |/ __ \ \/ \/ / // | _// __ \ \/ / |/ __ \ \/ \/ /

52
models/issue.go

@ -1990,6 +1990,12 @@ type DependencyInfo struct {
repo_model.Repository `xorm:"extends"` repo_model.Repository `xorm:"extends"`
} }
// ParentInfo represents high level information about an issue which is a parent of another issue.
type ParentInfo struct {
Issue `xorm:"extends"`
repo_model.Repository `xorm:"extends"`
}
// getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author // getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author
func (issue *Issue) getParticipantIDsByIssue(e db.Engine) ([]int64, error) { func (issue *Issue) getParticipantIDsByIssue(e db.Engine) ([]int64, error) {
if issue == nil { if issue == nil {
@ -2048,6 +2054,42 @@ func (issue *Issue) getBlockingDependencies(e db.Engine) (issueDeps []*Dependenc
return issueDeps, err return issueDeps, err
} }
// Get Blocked By Parents, aka all issues this issue is blocked by.
func (issue *Issue) getBlockedByParents(e db.Engine) (issueParents []*ParentInfo, err error) {
err = e.
Table("issue").
Join("INNER", "repository", "repository.id = issue.repo_id").
Join("INNER", "issue_parent", "issue_parent.parent_id = issue.id").
Where("issue_id = ?", issue.ID).
// sort by repo id then created date, with the issues of the same repo at the beginning of the list
OrderBy("CASE WHEN issue.repo_id = " + strconv.FormatInt(issue.RepoID, 10) + " THEN 0 ELSE issue.repo_id END, issue.created_unix DESC").
Find(&issueParents)
for _, parentInfo := range issueParents {
parentInfo.Issue.Repo = &parentInfo.Repository
}
return issueParents, err
}
// Get Blocking Parents, aka all issues this issue blocks.
func (issue *Issue) getBlockingParents(e db.Engine) (issueParents []*ParentInfo, err error) {
err = e.
Table("issue").
Join("INNER", "repository", "repository.id = issue.repo_id").
Join("INNER", "issue_parent", "issue_parent.issue_id = issue.id").
Where("parent_id = ?", issue.ID).
// sort by repo id then created date, with the issues of the same repo at the beginning of the list
OrderBy("CASE WHEN issue.repo_id = " + strconv.FormatInt(issue.RepoID, 10) + " THEN 0 ELSE issue.repo_id END, issue.created_unix DESC").
Find(&issueParents)
for _, parentInfo := range issueParents {
parentInfo.Issue.Repo = &parentInfo.Repository
}
return issueParents, err
}
// BlockedByDependencies finds all Dependencies an issue is blocked by // BlockedByDependencies finds all Dependencies an issue is blocked by
func (issue *Issue) BlockedByDependencies() ([]*DependencyInfo, error) { func (issue *Issue) BlockedByDependencies() ([]*DependencyInfo, error) {
return issue.getBlockedByDependencies(db.GetEngine(db.DefaultContext)) return issue.getBlockedByDependencies(db.GetEngine(db.DefaultContext))
@ -2058,6 +2100,16 @@ func (issue *Issue) BlockingDependencies() ([]*DependencyInfo, error) {
return issue.getBlockingDependencies(db.GetEngine(db.DefaultContext)) return issue.getBlockingDependencies(db.GetEngine(db.DefaultContext))
} }
// BlockedByParents finds all Parents an issue is blocked by
func (issue *Issue) BlockedByParents() ([]*ParentInfo, error) {
return issue.getBlockedByParents(db.GetEngine(db.DefaultContext))
}
// BlockingParents returns all blocking dependencies, aka all other issues a given issue blocks
func (issue *Issue) BlockingParents() ([]*ParentInfo, error) {
return issue.getBlockingParents(db.GetEngine(db.DefaultContext))
}
func (issue *Issue) updateClosedNum(ctx context.Context) (err error) { func (issue *Issue) updateClosedNum(ctx context.Context) (err error) {
if issue.IsPull { if issue.IsPull {
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls")

52
models/issue_comment.go

@ -108,6 +108,10 @@ const (
CommentTypeDismissReview CommentTypeDismissReview
// 33 Change issue ref // 33 Change issue ref
CommentTypeChangeIssueRef CommentTypeChangeIssueRef
// 34 Parent added
CommentTypeAddParent
// 35 Parent removed
CommentTypeRemoveParent
) )
var commentStrings = []string{ var commentStrings = []string{
@ -145,6 +149,8 @@ var commentStrings = []string{
"project_board", "project_board",
"dismiss_review", "dismiss_review",
"change_issue_ref", "change_issue_ref",
"add_parent",
"remove_parent",
} }
func (t CommentType) String() string { func (t CommentType) String() string {
@ -224,6 +230,8 @@ type Comment struct {
NewRef string NewRef string
DependentIssueID int64 DependentIssueID int64
DependentIssue *Issue `xorm:"-"` DependentIssue *Issue `xorm:"-"`
ParentIssueID int64
ParentIssue *Issue `xorm:"-"`
CommitID int64 CommitID int64
Line int64 // - previous line / + proposed line Line int64 // - previous line / + proposed line
@ -619,6 +627,15 @@ func (c *Comment) LoadDepIssueDetails() (err error) {
return err return err
} }
// LoadParentIssueDetails loads Parent Issue Details
func (c *Comment) LoadParentIssueDetails() (err error) {
if c.ParentIssueID <= 0 || c.ParentIssue != nil {
return nil
}
c.ParentIssue, err = getIssueByID(db.GetEngine(db.DefaultContext), c.ParentIssueID)
return err
}
// LoadTime loads the associated time for a CommentTypeAddTimeManual // LoadTime loads the associated time for a CommentTypeAddTimeManual
func (c *Comment) LoadTime() error { func (c *Comment) LoadTime() error {
if c.Time != nil || c.TimeID == 0 { if c.Time != nil || c.TimeID == 0 {
@ -789,6 +806,7 @@ func createComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
OldRef: opts.OldRef, OldRef: opts.OldRef,
NewRef: opts.NewRef, NewRef: opts.NewRef,
DependentIssueID: opts.DependentIssueID, DependentIssueID: opts.DependentIssueID,
ParentIssueID: opts.ParentIssueID,
TreePath: opts.TreePath, TreePath: opts.TreePath,
ReviewID: opts.ReviewID, ReviewID: opts.ReviewID,
Patch: opts.Patch, Patch: opts.Patch,
@ -933,6 +951,39 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is
return return
} }
// Creates issue parent comment
func createIssueParentComment(ctx context.Context, doer *user_model.User, issue, parentIssue *Issue, add bool) (err error) {
cType := CommentTypeAddParent
if !add {
cType = CommentTypeRemoveParent
}
if err = issue.loadRepo(ctx); err != nil {
return
}
// Make two comments, one in each issue
opts := &CreateCommentOptions{
Type: cType,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
ParentIssueID: parentIssue.ID,
}
if _, err = createComment(ctx, opts); err != nil {
return
}
opts = &CreateCommentOptions{
Type: cType,
Doer: doer,
Repo: issue.Repo,
Issue: parentIssue,
ParentIssueID: issue.ID,
}
_, err = createComment(ctx, opts)
return
}
// CreateCommentOptions defines options for creating comment // CreateCommentOptions defines options for creating comment
type CreateCommentOptions struct { type CreateCommentOptions struct {
Type CommentType Type CommentType
@ -942,6 +993,7 @@ type CreateCommentOptions struct {
Label *Label Label *Label
DependentIssueID int64 DependentIssueID int64
ParentIssueID int64
OldMilestoneID int64 OldMilestoneID int64
MilestoneID int64 MilestoneID int64
OldProjectID int64 OldProjectID int64

67
models/issue_comment_list.go

@ -395,6 +395,69 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
return nil return nil
} }
func (comments CommentList) getParentIssueIDs() []int64 {
ids := make(map[int64]struct{}, len(comments))
for _, comment := range comments {
if comment.ParentIssue != nil {
continue
}
if _, ok := ids[comment.ParentIssueID]; !ok {
ids[comment.ParentIssueID] = struct{}{}
}
}
return keysInt64(ids)
}
func (comments CommentList) loadParentIssues(ctx context.Context) error {
if len(comments) == 0 {
return nil
}
e := db.GetEngine(ctx)
issueIDs := comments.getParentIssueIDs()
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
limit := defaultMaxInSize
if left < limit {
limit = left
}
rows, err := e.
In("id", issueIDs[:limit]).
Rows(new(Issue))
if err != nil {
return err
}
for rows.Next() {
var issue Issue
err = rows.Scan(&issue)
if err != nil {
_ = rows.Close()
return err
}
issues[issue.ID] = &issue
}
_ = rows.Close()
left -= limit
issueIDs = issueIDs[limit:]
}
for _, comment := range comments {
if comment.ParentIssue == nil {
comment.ParentIssue = issues[comment.ParentIssueID]
if comment.ParentIssue != nil {
if err := comment.ParentIssue.loadRepo(ctx); err != nil {
return err
}
}
}
}
return nil
}
func (comments CommentList) loadAttachments(e db.Engine) (err error) { func (comments CommentList) loadAttachments(e db.Engine) (err error) {
if len(comments) == 0 { if len(comments) == 0 {
return nil return nil
@ -528,6 +591,10 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
return return
} }
if err = comments.loadParentIssues(ctx); err != nil {
return
}
return nil return nil
} }

134
models/issue_parent.go

@ -0,0 +1,134 @@
// Copyright 2018-2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
)
// IssueParent represents an issue parent
type IssueParent struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
IssueID int64 `xorm:"UNIQUE(issue_parent) NOT NULL"`
ParentID int64 `xorm:"UNIQUE(issue_parent) NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
func init() {
db.RegisterModel(new(IssueParent))
}
// ParentType Defines Parent Type Constants
type ParentType int
// Define Parent Types
const (
ParentTypeFather ParentType = iota
ParentTypeChild
)
// CreateIssueParent creates a new parent for an issue
func CreateIssueParent(user *user_model.User, issue, parent *Issue) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
sess := db.GetEngine(ctx)
// Check if it aleready exists
exists, err := issueParentExists(sess, issue.ID, parent.ID)
if err != nil {
return err
}
if exists {
return ErrParentExists{issue.ID, parent.ID}
}
// And if it would be circular
circular, err := issueParentExists(sess, parent.ID, issue.ID)
if err != nil {
return err
}
if circular {
return ErrCircularParent{issue.ID, parent.ID}
}
if err := db.Insert(ctx, &IssueParent{
UserID: user.ID,
IssueID: issue.ID,
ParentID: parent.ID,
}); err != nil {
return err
}
// Add comment referencing the new parent
if err = createIssueParentComment(ctx, user, issue, parent, true); err != nil {
return err
}
return committer.Commit()
}
// RemoveIssueParent removes a parent from an issue
func RemoveIssueParent(user *user_model.User, issue, parent *Issue, parentType ParentType) (err error) {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
var issueParentToDelete IssueParent
switch parentType {
case ParentTypeFather:
issueParentToDelete = IssueParent{IssueID: issue.ID, ParentID: parent.ID}
case ParentTypeChild:
issueParentToDelete = IssueParent{IssueID: parent.ID, ParentID: issue.ID}
default:
return ErrUnknownParentType{parentType}
}
affected, err := db.GetEngine(ctx).Delete(&issueParentToDelete)
if err != nil {
return err
}
// If we deleted nothing, the parent did not exist
if affected <= 0 {
return ErrParentNotExists{issue.ID, parent.ID}
}
// Add comment referencing the removed parent
if err = createIssueParentComment(ctx, user, issue, parent, false); err != nil {
return err
}
return committer.Commit()
}
// Check if the parent already exists
func issueParentExists(e db.Engine, issueID, depID int64) (bool, error) {
return e.Where("(issue_id = ? AND parent_id = ?)", issueID, depID).Exist(&IssueParent{})
}
// IssueNoParentsLeft checks if issue can be closed
func IssueNoParentsLeft(issue *Issue) (bool, error) {
return issueNoParentsLeft(db.GetEngine(db.DefaultContext), issue)
}
func issueNoParentsLeft(e db.Engine, issue *Issue) (bool, error) {
exists, err := e.
Table("issue_parent").
Select("issue.*").
Join("INNER", "issue", "issue.id = issue_parent.parent_id").
Where("issue_parent.issue_id = ?", issue.ID).
And("issue.is_closed = ?", "0").
Exist(&Issue{})
return !exists, err
}

61
models/issue_parent_test.go

@ -0,0 +1,61 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func TestCreateIssueParent(t *testing.T) {
// Prepare
assert.NoError(t, unittest.PrepareTestDatabase())
user1, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
assert.NoError(t, err)
// Create a dependency and check if it was successful
err = CreateIssueParent(user1, issue1, issue2)
assert.NoError(t, err)
// Do it again to see if it will check if the dependency already exists
err = CreateIssueParent(user1, issue1, issue2)
assert.Error(t, err)
assert.True(t, IsErrParentExists(err))
// Check for circular dependencies
err = CreateIssueParent(user1, issue2, issue1)
assert.Error(t, err)
assert.True(t, IsErrCircularParent(err))
_ = unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddParent, PosterID: user1.ID, IssueID: issue1.ID})
// Check if dependencies left is correct
left, err := IssueNoParentsLeft(issue1)
assert.NoError(t, err)
assert.False(t, left)
// Close #2 and check again
_, err = issue2.ChangeStatus(user1, true)
assert.NoError(t, err)
left, err = IssueNoParentsLeft(issue1)
assert.NoError(t, err)
assert.True(t, left)
// Test removing the dependency
err = RemoveIssueParent(user1, issue1, issue2, ParentTypeFather)
assert.NoError(t, err)
}

3
models/migrations/v70.go

@ -102,6 +102,9 @@ func addIssueDependencies(x *xorm.Engine) (err error) {
if _, ok := unit.Config["EnableDependencies"]; !ok { if _, ok := unit.Config["EnableDependencies"]; !ok {
unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
} }
if _, ok := unit.Config["EnableParents"]; !ok {
unit.Config["EnableParents"] = setting.Service.DefaultEnableParents
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil { if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err return err
} }

4
models/org_team.go

@ -691,8 +691,8 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
return errors.New("empty team name") return errors.New("empty team name")
} }
if len(t.Description) > 255 { if len(t.Description) > 1024 {
t.Description = t.Description[:255] t.Description = t.Description[:1024]
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext()

5
models/repo.go

@ -515,6 +515,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
EnableTimetracker: setting.Service.DefaultEnableTimetracking, EnableTimetracker: setting.Service.DefaultEnableTimetracking,
AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime, AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
EnableDependencies: setting.Service.DefaultEnableDependencies, EnableDependencies: setting.Service.DefaultEnableDependencies,
EnableParents: setting.Service.DefaultEnableParents,
}, },
}) })
} else if tp == unit.TypePullRequests { } else if tp == unit.TypePullRequests {
@ -635,8 +636,8 @@ func DecrementRepoForkNum(ctx context.Context, repoID int64) error {
func updateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) { func updateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
repo.LowerName = strings.ToLower(repo.Name) repo.LowerName = strings.ToLower(repo.Name)
if utf8.RuneCountInString(repo.Description) > 255 { if utf8.RuneCountInString(repo.Description) > 1024 {
repo.Description = string([]rune(repo.Description)[:255]) repo.Description = string([]rune(repo.Description)[:1024])
} }
if utf8.RuneCountInString(repo.Website) > 255 { if utf8.RuneCountInString(repo.Website) > 255 {
repo.Website = string([]rune(repo.Website)[:255]) repo.Website = string([]rune(repo.Website)[:255])

16
models/repo/issue.go

@ -70,3 +70,19 @@ func (repo *Repository) IsDependenciesEnabledCtx(ctx context.Context) bool {
} }
return u.IssuesConfig().EnableDependencies return u.IssuesConfig().EnableDependencies
} }
// IsParentsEnabled returns if parents are enabled and returns the default setting if not set.
func (repo *Repository) IsParentsEnabled() bool {
return repo.IsParentsEnabledCtx(db.DefaultContext)
}
// IsParentsEnabledCtx returns if parents are enabled and returns the default setting if not set.
func (repo *Repository) IsParentsEnabledCtx(ctx context.Context) bool {
var u *RepoUnit
var err error
if u, err = repo.GetUnitCtx(ctx, unit.TypeIssues); err != nil {
log.Trace("%s", err)
return setting.Service.DefaultEnableParents
}
return u.IssuesConfig().EnableParents
}

1
models/repo/repo_unit.go

@ -94,6 +94,7 @@ type IssuesConfig struct {
EnableTimetracker bool EnableTimetracker bool
AllowOnlyContributorsToTrackTime bool AllowOnlyContributorsToTrackTime bool
EnableDependencies bool EnableDependencies bool
EnableParents bool
} }
// FromDB fills up a IssuesConfig from serialized format. // FromDB fills up a IssuesConfig from serialized format.

24
models/user/user.go

@ -89,16 +89,17 @@ type User struct {
// is to change his/her password after registration. // is to change his/her password after registration.
MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
LoginType auth.Type LoginType auth.Type
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
LoginName string LoginName string
Type UserType Type UserType
Location string Location string
Website string LocationCoordinate string
Rands string `xorm:"VARCHAR(32)"` Website string
Salt string `xorm:"VARCHAR(32)"` Rands string `xorm:"VARCHAR(32)"`
Language string `xorm:"VARCHAR(5)"` Salt string `xorm:"VARCHAR(32)"`
Description string Language string `xorm:"VARCHAR(5)"`
Description string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@ -187,8 +188,9 @@ func (u *User) BeforeUpdate() {
u.LowerName = strings.ToLower(u.Name) u.LowerName = strings.ToLower(u.Name)
u.Location = base.TruncateString(u.Location, 255) u.Location = base.TruncateString(u.Location, 255)
u.LocationCoordinate = base.TruncateString(u.LocationCoordinate, 255)
u.Website = base.TruncateString(u.Website, 255) u.Website = base.TruncateString(u.Website, 255)
u.Description = base.TruncateString(u.Description, 255) u.Description = base.TruncateString(u.Description, 1024)
} }
// AfterLoad is invoked from XORM after filling all the fields of this object. // AfterLoad is invoked from XORM after filling all the fields of this object.

5
modules/context/repo.go

@ -164,6 +164,11 @@ func (r *Repository) CanCreateIssueDependencies(user *user_model.User, isPull bo
return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull) return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
} }
// CanCreateIssueParents returns whether or not a user can create parents.
func (r *Repository) CanCreateIssueParents(user *user_model.User, isPull bool) bool {
return r.Repository.IsParentsEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
}
// GetCommitsCount returns cached commit count for current view // GetCommitsCount returns cached commit count for current view
func (r *Repository) GetCommitsCount() (int64, error) { func (r *Repository) GetCommitsCount() (int64, error) {
var contextName string var contextName string

1
modules/convert/convert.go

@ -289,6 +289,7 @@ func ToOrganization(org *models.Organization) *api.Organization {
Description: org.Description, Description: org.Description,
Website: org.Website, Website: org.Website,
Location: org.Location, Location: org.Location,
LocationCoordinate: org.LocationCoordinate,
Visibility: org.Visibility.String(), Visibility: org.Visibility.String(),
RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess, RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess,
} }

10
modules/convert/issue_comment.go

@ -52,6 +52,12 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo
return nil return nil
} }
err = c.LoadParentIssueDetails()
if err != nil {
log.Error("LoadParentIssueDetails: %v", err)
return nil
}
err = c.LoadTime() err = c.LoadTime()
if err != nil { if err != nil {
log.Error("LoadTime: %v", err) log.Error("LoadTime: %v", err)
@ -163,5 +169,9 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo
comment.DependentIssue = ToAPIIssue(c.DependentIssue) comment.DependentIssue = ToAPIIssue(c.DependentIssue)
} }
if c.ParentIssue != nil {
comment.ParentIssue = ToAPIIssue(c.ParentIssue)
}
return comment return comment
} }

1
modules/convert/repository.go

@ -51,6 +51,7 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo
EnableTimeTracker: config.EnableTimetracker, EnableTimeTracker: config.EnableTimetracker,
AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime,
EnableIssueDependencies: config.EnableDependencies, EnableIssueDependencies: config.EnableDependencies,
EnableIssueParents: config.EnableParents,
} }
} else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil { } else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil {
config := unit.ExternalTrackerConfig() config := unit.ExternalTrackerConfig()

2
modules/convert/user.go

@ -55,6 +55,7 @@ func toUser(user *user_model.User, signed, authed bool) *api.User {
Created: user.CreatedUnix.AsTime(), Created: user.CreatedUnix.AsTime(),
Restricted: user.IsRestricted, Restricted: user.IsRestricted,
Location: user.Location, Location: user.Location,
LocationCoordinate: user.LocationCoordinate,
Website: user.Website, Website: user.Website,
Description: user.Description, Description: user.Description,
// counter's // counter's
@ -87,6 +88,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings {
FullName: user.FullName, FullName: user.FullName,
Website: user.Website, Website: user.Website,
Location: user.Location, Location: user.Location,
LocationCoordinate: user.LocationCoordinate,
Language: user.Language, Language: user.Language,
Description: user.Description, Description: user.Description,
Theme: user.Theme, Theme: user.Theme,

4
modules/doctor/fix16961.go

@ -206,6 +206,10 @@ func fixIssuesConfig16961(bs []byte, cfg *repo_model.IssuesConfig) (fixed bool,
if parseErr != nil { if parseErr != nil {
return return
} }
cfg.EnableParents, parseErr = parseBool16961(parts[3])
if parseErr != nil {
return
}
return true, nil return true, nil
} }

4
modules/doctor/fix16961_test.go

@ -237,11 +237,12 @@ func Test_fixIssuesConfig_16961(t *testing.T) {
}{ }{
{ {
name: "normal", name: "normal",
bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true}`, bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true,"EnableParents":true}`,
expected: repo_model.IssuesConfig{ expected: repo_model.IssuesConfig{
EnableTimetracker: true, EnableTimetracker: true,
AllowOnlyContributorsToTrackTime: true, AllowOnlyContributorsToTrackTime: true,
EnableDependencies: true, EnableDependencies: true,
EnableParents: true,
}, },
}, },
{ {
@ -251,6 +252,7 @@ func Test_fixIssuesConfig_16961(t *testing.T) {
EnableTimetracker: true, EnableTimetracker: true,
AllowOnlyContributorsToTrackTime: true, AllowOnlyContributorsToTrackTime: true,
EnableDependencies: true, EnableDependencies: true,
EnableParents: true,
}, },
wantFixed: true, wantFixed: true,
}, },

4
modules/setting/service.go

@ -53,7 +53,9 @@ var Service = struct {
EnableTimetracking bool EnableTimetracking bool
DefaultEnableTimetracking bool DefaultEnableTimetracking bool
DefaultEnableDependencies bool DefaultEnableDependencies bool
DefaultEnableParents bool
AllowCrossRepositoryDependencies bool AllowCrossRepositoryDependencies bool
AllowCrossRepositoryParents bool
DefaultAllowOnlyContributorsToTrackTime bool DefaultAllowOnlyContributorsToTrackTime bool
NoReplyAddress string NoReplyAddress string
EnableUserHeatmap bool EnableUserHeatmap bool
@ -141,7 +143,9 @@ func newService() {
Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true)
} }
Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true) Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true)
Service.DefaultEnableParents = sec.Key("DEFAULT_ENABLE_PARENTS").MustBool(true)
Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true) Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true)
Service.AllowCrossRepositoryParents = sec.Key("ALLOW_CROSS_REPOSITORY_PARENTS").MustBool(true)
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true) Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain) Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain)
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true) Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)

1
modules/structs/admin_user.go

@ -36,6 +36,7 @@ type EditUserOption struct {
MustChangePassword *bool `json:"must_change_password"` MustChangePassword *bool `json:"must_change_password"`
Website *string `json:"website" binding:"OmitEmpty;ValidUrl;MaxSize(255)"` Website *string `json:"website" binding:"OmitEmpty;ValidUrl;MaxSize(255)"`
Location *string `json:"location" binding:"MaxSize(50)"` Location *string `json:"location" binding:"MaxSize(50)"`
LocationCoordinate *string `json:"location_coordinate" binding:"MaxSize(255)"`
Description *string `json:"description" binding:"MaxSize(255)"` Description *string `json:"description" binding:"MaxSize(255)"`
Active *bool `json:"active"` Active *bool `json:"active"`
Admin *bool `json:"admin"` Admin *bool `json:"admin"`

2
modules/structs/issue_comment.go

@ -79,4 +79,6 @@ type TimelineComment struct {
ResolveDoer *User `json:"resolve_doer"` ResolveDoer *User `json:"resolve_doer"`
DependentIssue *Issue `json:"dependent_issue"` DependentIssue *Issue `json:"dependent_issue"`
ParentIssue *Issue `json:"parent_issue"`
} }

7
modules/structs/org.go

@ -13,6 +13,7 @@ type Organization struct {
Description string `json:"description"` Description string `json:"description"`
Website string `json:"website"` Website string `json:"website"`
Location string `json:"location"` Location string `json:"location"`
LocationCoordinate string `json:"location_coordinate"`
Visibility string `json:"visibility"` Visibility string `json:"visibility"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
} }
@ -31,9 +32,10 @@ type CreateOrgOption struct {
// required: true // required: true
UserName string `json:"username" binding:"Required"` UserName string `json:"username" binding:"Required"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
Website string `json:"website" binding:"ValidUrl;MaxSize(255)"` Website string `json:"website" binding:"ValidUrl;MaxSize(255)"`
Location string `json:"location" binding:"MaxSize(50)"` Location string `json:"location" binding:"MaxSize(50)"`
LocationCoordinate string `json:"location_coordinate" binding:"MaxSize(255)"`
// possible values are `public` (default), `limited` or `private` // possible values are `public` (default), `limited` or `private`
// enum: public,limited,private // enum: public,limited,private
Visibility string `json:"visibility" binding:"In(,public,limited,private)"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
@ -45,9 +47,10 @@ type CreateOrgOption struct {
// EditOrgOption options for editing an organization // EditOrgOption options for editing an organization
type EditOrgOption struct { type EditOrgOption struct {
FullName string `json:"full_name"` FullName string `json:"full_name"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
Website string `json:"website" binding:"ValidUrl;MaxSize(255)"` Website string `json:"website" binding:"ValidUrl;MaxSize(255)"`
Location string `json:"location" binding:"MaxSize(50)"` Location string `json:"location" binding:"MaxSize(50)"`
LocationCoordinate string `json:"location_coordinate" binding:"MaxSize(255)"`
// possible values are `public`, `limited` or `private` // possible values are `public`, `limited` or `private`
// enum: public,limited,private // enum: public,limited,private
Visibility string `json:"visibility" binding:"In(,public,limited,private)"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"`

4
modules/structs/org_team.go

@ -26,7 +26,7 @@ type Team struct {
type CreateTeamOption struct { type CreateTeamOption struct {
// required: true // required: true
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
IncludesAllRepositories bool `json:"includes_all_repositories"` IncludesAllRepositories bool `json:"includes_all_repositories"`
// enum: read,write,admin // enum: read,write,admin
Permission string `json:"permission"` Permission string `json:"permission"`
@ -42,7 +42,7 @@ type CreateTeamOption struct {
type EditTeamOption struct { type EditTeamOption struct {
// required: true // required: true
Name string `json:"name" binding:"AlphaDashDot;MaxSize(30)"` Name string `json:"name" binding:"AlphaDashDot;MaxSize(30)"`
Description *string `json:"description" binding:"MaxSize(255)"` Description *string `json:"description" binding:"MaxSize(1024)"`
IncludesAllRepositories *bool `json:"includes_all_repositories"` IncludesAllRepositories *bool `json:"includes_all_repositories"`
// enum: read,write,admin // enum: read,write,admin
Permission string `json:"permission"` Permission string `json:"permission"`

10
modules/structs/repo.go

@ -25,6 +25,8 @@ type InternalTracker struct {
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"` AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
// Enable dependencies for issues and pull requests (Built-in issue tracker) // Enable dependencies for issues and pull requests (Built-in issue tracker)
EnableIssueDependencies bool `json:"enable_issue_dependencies"` EnableIssueDependencies bool `json:"enable_issue_dependencies"`
// Enable parents for issues and pull requests (Built-in issue tracker)
EnableIssueParents bool `json:"enable_issue_parents"`
} }
// ExternalTracker represents settings for external tracker // ExternalTracker represents settings for external tracker
@ -107,7 +109,7 @@ type CreateRepoOption struct {
// unique: true // unique: true
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
// Description of the repository to create // Description of the repository to create
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
// Whether the repository is private // Whether the repository is private
Private bool `json:"private"` Private bool `json:"private"`
// Label-Set to use // Label-Set to use
@ -136,7 +138,7 @@ type EditRepoOption struct {
// unique: true // unique: true
Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"` Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"`
// a short description of the repository. // a short description of the repository.
Description *string `json:"description,omitempty" binding:"MaxSize(255)"` Description *string `json:"description,omitempty" binding:"MaxSize(1024)"`
// a URL with more information about the repository. // a URL with more information about the repository.
Website *string `json:"website,omitempty" binding:"MaxSize(255)"` Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
// either `true` to make the repository private or `false` to make it public. // either `true` to make the repository private or `false` to make it public.
@ -200,7 +202,7 @@ type GenerateRepoOption struct {
// unique: true // unique: true
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
// Description of the repository to create // Description of the repository to create
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
// Whether the repository is private // Whether the repository is private
Private bool `json:"private"` Private bool `json:"private"`
// include git content of default branch in template repo // include git content of default branch in template repo
@ -309,7 +311,7 @@ type MigrateRepoOptions struct {
LFS bool `json:"lfs"` LFS bool `json:"lfs"`
LFSEndpoint string `json:"lfs_endpoint"` LFSEndpoint string `json:"lfs_endpoint"`
Private bool `json:"private"` Private bool `json:"private"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
Wiki bool `json:"wiki"` Wiki bool `json:"wiki"`
Milestones bool `json:"milestones"` Milestones bool `json:"milestones"`
Labels bool `json:"labels"` Labels bool `json:"labels"`

6
modules/structs/user.go

@ -39,6 +39,8 @@ type User struct {
ProhibitLogin bool `json:"prohibit_login"` ProhibitLogin bool `json:"prohibit_login"`
// the user's location // the user's location
Location string `json:"location"` Location string `json:"location"`
// the user's location coordinate
LocationCoordinate string `json:"location_coordinate"`
// the user's website // the user's website
Website string `json:"website"` Website string `json:"website"`
// the user's description // the user's description
@ -69,6 +71,7 @@ type UserSettings struct {
Website string `json:"website"` Website string `json:"website"`
Description string `json:"description"` Description string `json:"description"`
Location string `json:"location"` Location string `json:"location"`
LocationCoordinate string `json:"location_coordinate"`
Language string `json:"language"` Language string `json:"language"`
Theme string `json:"theme"` Theme string `json:"theme"`
DiffViewStyle string `json:"diff_view_style"` DiffViewStyle string `json:"diff_view_style"`
@ -82,8 +85,9 @@ type UserSettings struct {
type UserSettingsOptions struct { type UserSettingsOptions struct {
FullName *string `json:"full_name" binding:"MaxSize(100)"` FullName *string `json:"full_name" binding:"MaxSize(100)"`
Website *string `json:"website" binding:"OmitEmpty;ValidUrl;MaxSize(255)"` Website *string `json:"website" binding:"OmitEmpty;ValidUrl;MaxSize(255)"`
Description *string `json:"description" binding:"MaxSize(255)"` Description *string `json:"description" binding:"MaxSize(1024)"`
Location *string `json:"location" binding:"MaxSize(50)"` Location *string `json:"location" binding:"MaxSize(50)"`
LocationCoordinate *string `json:"location_coordinate" binding:"MaxSize(255)"`
Language *string `json:"language"` Language *string `json:"language"`
Theme *string `json:"theme"` Theme *string `json:"theme"`
DiffViewStyle *string `json:"diff_view_style"` DiffViewStyle *string `json:"diff_view_style"`

571
options/locale/locale_ru-RU.ini

File diff suppressed because it is too large Load Diff

45
package-lock.json generated

@ -1711,6 +1711,8 @@
"version": "8.9.0", "version": "8.9.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0", "json-schema-traverse": "^1.0.0",
@ -1725,7 +1727,9 @@
"node_modules/ajv-formats/node_modules/json-schema-traverse": { "node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"optional": true,
"peer": true
}, },
"node_modules/ajv-keywords": { "node_modules/ajv-keywords": {
"version": "3.5.2", "version": "3.5.2",
@ -2119,13 +2123,19 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001301", "version": "1.0.30001374",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz",
"integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==",
"funding": { "funding": [
"type": "opencollective", {
"url": "https://opencollective.com/browserslist" "type": "opencollective",
} "url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
]
}, },
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
@ -11189,14 +11199,13 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"requires": { "requires": {},
"ajv": "^8.0.0"
},
"dependencies": { "dependencies": {
"ajv": { "ajv": {
"version": "8.9.0", "version": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz",
"integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==",
"optional": true,
"peer": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0", "json-schema-traverse": "^1.0.0",
@ -11207,7 +11216,9 @@
"json-schema-traverse": { "json-schema-traverse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"optional": true,
"peer": true
} }
} }
}, },
@ -11497,9 +11508,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001301", "version": "1.0.30001374",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz",
"integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==" "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw=="
}, },
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.2",

BIN
public/img/logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

108
public/img/logo.svg

@ -1 +1,107 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640" xml:space="preserve" width="32" height="32"><path style="fill:#fff" d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12z"/><path style="fill:#609926" d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6zM125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1zm300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1z"/><path style="fill:#609926" d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8-1.9 8 2 16.3 9.1 20 7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3 7.8 4 17.4 1.7 22.5-5.3 5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8l-24.6 50.4z"/></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 1133.8586 755.90533"
height="755.90533"
width="1133.8586"
xml:space="preserve"
id="svg2"
version="1.1"
sodipodi:docname="Мирокод 3.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview98"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.97983995"
inkscape:cx="566.92932"
inkscape:cy="377.61269"
inkscape:window-width="1280"
inkscape:window-height="981"
inkscape:window-x="-2"
inkscape:window-y="-2"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,0 H 726.398 V 178.782 H 0 Z" /></clipPath><clipPath
id="clipPath40"
clipPathUnits="userSpaceOnUse"><path
id="path38"
d="M 0,566.929 H 850.394 V 0 H 0 Z" /></clipPath></defs><g
id="g457"><g
transform="matrix(1.3333333,0,0,-1.3333333,198.4164,535.00667)"
id="g26"><path
id="path28"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 H 14.864 L 16.008,-0.572 39.447,-28.584 62.886,-0.572 64.03,0 h 14.864 l 0.571,-0.572 v -78.894 l -0.571,-0.572 H 64.03 l -0.572,0.572 v 54.883 L 40.019,-52.024 h -1.144 l -23.439,27.441 v -54.883 l -0.572,-0.572 H 0 l -0.572,0.572 v 78.894 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,321.12346,535.00667)"
id="g30"><path
id="path32"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 h 14.864 l 0.571,-0.572 V -52.596 L 55.454,-0.572 56.598,0 h 15.436 l 0.571,-0.572 v -78.894 l -0.571,-0.572 H 57.169 l -0.571,0.572 v 52.024 L 16.579,-79.466 15.435,-80.038 H 0 l -0.572,0.572 v 78.894 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,455.26466,556.35053)"
id="g42"><path
id="path44"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 v -19.437 h 8.003 c 8.004,0 12.006,3.238 12.006,9.718 C 20.009,-3.241 16.007,0 8.003,0 Z M -15.436,16.008 H 9.147 c 8.765,0 15.722,-2.498 20.867,-7.49 5.145,-4.993 7.717,-11.073 7.717,-18.237 0,-7.165 -2.572,-13.245 -7.717,-18.237 -5.145,-4.993 -12.102,-7.489 -20.867,-7.489 H 0 v -28.013 l -0.572,-0.572 h -14.864 l -0.572,0.572 v 78.894 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,592.21265,564.50613)"
id="g46"><path
id="path48"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -4.765,4.839 -10.576,7.26 -17.437,7.26 -6.86,0 -12.673,-2.421 -17.436,-7.26 -4.765,-4.842 -7.147,-10.806 -7.147,-17.895 0,-7.089 2.382,-13.053 7.147,-17.894 4.763,-4.841 10.576,-7.26 17.436,-7.26 6.861,0 12.672,2.419 17.437,7.26 4.763,4.841 7.146,10.805 7.146,17.894 C 7.146,-10.806 4.763,-4.842 0,0 m -47.394,11.72 c 8.233,8.078 18.218,12.119 29.957,12.119 11.738,0 21.724,-4.041 29.957,-12.119 8.232,-8.081 12.348,-17.951 12.348,-29.615 0,-11.662 -4.116,-21.534 -12.348,-29.613 -8.233,-8.081 -18.219,-12.12 -29.957,-12.12 -11.739,0 -21.724,4.039 -29.957,12.12 -8.232,8.079 -12.348,17.951 -12.348,29.613 0,11.664 4.116,21.534 12.348,29.615" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,637.04732,535.00667)"
id="g50"><path
id="path52"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 h 14.863 l 0.573,-0.572 v -27.441 h 6.86 L 47.45,-0.572 48.594,0 h 17.722 l 0.572,-0.572 -31.444,-34.302 33.731,-44.592 -0.571,-0.572 H 50.881 l -1.145,0.572 -27.44,35.445 h -6.86 v -35.445 l -0.573,-0.572 H 0 l -0.572,0.572 v 78.894 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,803.04985,564.50613)"
id="g54"><path
id="path56"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -4.765,4.839 -10.576,7.26 -17.437,7.26 -6.86,0 -12.672,-2.421 -17.436,-7.26 -4.765,-4.842 -7.147,-10.806 -7.147,-17.895 0,-7.089 2.382,-13.053 7.147,-17.894 4.764,-4.841 10.576,-7.26 17.436,-7.26 6.861,0 12.672,2.419 17.437,7.26 4.764,4.841 7.146,10.805 7.146,17.894 C 7.146,-10.806 4.764,-4.842 0,0 m -47.394,11.72 c 8.234,8.078 18.218,12.119 29.957,12.119 11.739,0 21.725,-4.041 29.958,-12.119 8.232,-8.081 12.348,-17.951 12.348,-29.615 0,-11.662 -4.116,-21.534 -12.348,-29.613 -8.233,-8.081 -18.219,-12.12 -29.958,-12.12 -11.739,0 -21.723,4.039 -29.957,12.12 -8.232,8.079 -12.348,17.951 -12.348,29.613 0,11.664 4.116,21.534 12.348,29.615" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,884.05371,556.35053)"
id="g58"><path
id="path60"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -4.464,-43.558 c 0,-0.154 -0.077,-0.801 -0.229,-1.944 -0.154,-1.144 -0.343,-2.287 -0.572,-3.43 h 30.3 V 0 Z m -13.72,16.008 h 54.191 l 0.572,-0.572 v -64.368 h 8.575 l 0.572,-0.571 v -36.264 l -0.572,-0.571 H 34.754 l -0.571,0.571 v 20.828 h -51.454 v -20.828 l -0.571,-0.571 h -14.863 l -0.572,0.571 v 36.264 l 0.572,0.571 h 9.832 c 0.686,1.372 1.486,4.039 2.401,8.004 l 6.18,56.364 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,565.51465,132.72228)"
id="g186"><path
id="path188"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 17.049,-7.257 33.428,-15.985 48.958,-26.035 l 2.688,25.574 C 35.257,9.378 18.003,17.924 0,24.988 -18.068,17.899 -35.381,9.316 -51.823,-0.567 l 2.688,-25.581 C -33.552,-16.052 -17.114,-7.284 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,662.47692,431.28934)"
id="g190"><path
id="path192"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -18.016,-4.131 -36.423,-6.706 -54.983,-7.711 l 12.847,-22.254 c 19.435,1.7 38.419,4.969 56.803,9.739 C 24.999,-4.102 34,12.95 41.586,30.75 L 24.376,49.864 C 17.707,32.6 9.563,15.903 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,722.35852,246.79974)"
id="g194"><path
id="path196"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -1.611,-18.497 -4.824,-36.788 -9.554,-54.656 l 25.172,5.35 c 4.228,18.498 6.994,37.552 8.137,57.044 C 11.508,22.622 -1.976,36.444 -16.502,49.1 L -39.975,38.649 C -25.615,26.958 -12.224,14.034 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,408.66052,246.79708)"
id="g198"><path
id="path200"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 12.203,14.009 25.569,26.915 39.902,38.589 l -23.469,10.45 c -14.5,-12.64 -27.96,-26.441 -40.188,-41.301 1.142,-19.466 3.901,-38.495 8.119,-56.968 l 25.17,-5.35 C 4.814,-36.736 1.609,-18.47 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,468.54465,431.28734)"
id="g202"><path
id="path204"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -9.588,15.947 -17.75,32.693 -24.43,50.008 L -41.645,30.889 c 7.6,-17.85 16.619,-34.95 26.979,-51.115 18.382,-4.769 37.363,-8.039 56.795,-9.74 L 54.977,-7.713 C 36.419,-6.707 18.016,-4.131 0,0" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

102
public/img/logo1.svg

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 1700.7867 377.95334"
height="377.95334"
width="1700.7867"
xml:space="preserve"
id="svg2"
version="1.1"
sodipodi:docname="Мирокод 2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview96"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.6532271"
inkscape:cx="850.39337"
inkscape:cy="188.29592"
inkscape:window-width="1280"
inkscape:window-height="981"
inkscape:window-x="-2"
inkscape:window-y="-2"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath26"
clipPathUnits="userSpaceOnUse"><path
id="path24"
d="M 0,283.465 H 1275.591 V 0 H 0 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,453.37466,90.731737)"
id="g12"><path
id="path14"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 H 22.79 L 24.544,-0.876 60.481,-43.827 96.421,-0.876 98.175,0 h 22.79 l 0.876,-0.876 v -120.965 l -0.876,-0.877 h -22.79 l -0.878,0.877 v 84.15 L 61.359,-79.767 h -1.754 l -35.939,42.076 v -84.15 l -0.876,-0.877 H 0 l -0.877,0.877 V -0.876 Z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,641.51665,90.731737)"
id="g16"><path
id="path18"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 h 22.79 l 0.876,-0.876 V -80.643 L 85.025,-0.876 86.778,0 h 23.668 l 0.876,-0.876 v -120.965 l -0.876,-0.877 H 87.655 l -0.877,0.877 v 79.766 l -61.358,-79.766 -1.754,-0.877 H 0 l -0.878,0.877 V -0.876 Z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,847.18851,123.45627)"
id="g28"><path
id="path30"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 v -29.803 h 12.271 c 12.273,0 18.409,4.966 18.409,14.902 C 30.68,-4.969 24.544,0 12.271,0 Z m -23.666,24.543 h 37.691 c 13.439,0 24.106,-3.829 31.995,-11.482 7.889,-7.656 11.833,-16.978 11.833,-27.962 0,-10.987 -3.944,-20.309 -11.833,-27.963 C 38.131,-50.52 27.464,-54.347 14.025,-54.347 H 0 v -42.951 l -0.876,-0.876 h -22.79 l -0.878,0.876 V 23.667 Z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,1057.165,135.96147)"
id="g32"><path
id="path34"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -7.305,7.419 -16.216,11.131 -26.735,11.131 -10.518,0 -19.431,-3.712 -26.734,-11.131 -7.305,-7.423 -10.958,-16.568 -10.958,-27.437 0,-10.869 3.653,-20.015 10.958,-27.436 7.303,-7.423 16.216,-11.132 26.734,-11.132 10.519,0 19.43,3.709 26.735,11.132 7.303,7.421 10.958,16.567 10.958,27.436 C 10.958,-16.568 7.303,-7.423 0,0 m -72.667,17.969 c 12.624,12.386 27.934,18.583 45.932,18.583 17.998,0 33.309,-6.197 45.931,-18.583 C 31.818,5.58 38.13,-9.554 38.13,-27.437 c 0,-17.881 -6.312,-33.018 -18.934,-45.405 -12.622,-12.39 -27.933,-18.583 -45.931,-18.583 -17.998,0 -33.308,6.193 -45.932,18.583 -12.622,12.387 -18.933,27.524 -18.933,45.405 0,17.883 6.311,33.017 18.933,45.406" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,1125.9085,90.731737)"
id="g36"><path
id="path38"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 h 22.79 l 0.876,-0.876 v -42.075 h 10.52 L 72.754,-0.876 74.507,0 h 27.174 l 0.876,-0.876 -48.211,-52.594 51.717,-68.371 -0.876,-0.877 H 78.014 l -1.754,0.877 -42.074,54.346 h -10.52 v -54.346 l -0.876,-0.877 H 0 l -0.877,0.877 V -0.876 Z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,1380.4345,135.96147)"
id="g40"><path
id="path42"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -7.306,7.419 -16.216,11.131 -26.735,11.131 -37.254,11.131 -46.167,7.419 -53.47,0 c -7.305,-7.423 -10.957,-16.568 -10.957,-27.437 0,-10.869 3.652,-20.015 10.957,-27.436 7.303,-7.423 16.216,-11.132 26.735,-11.132 10.519,0 19.429,3.709 26.735,11.132 7.303,7.421 10.957,16.567 10.957,27.436 C 10.957,-16.568 7.303,-7.423 0,0 m -72.667,17.969 c 12.623,12.386 27.933,18.583 45.932,18.583 17.997,0 33.309,-6.197 45.931,-18.583 C 31.818,5.58 38.13,-9.554 38.13,-27.437 c 0,-17.881 -6.312,-33.018 -18.934,-45.405 -12.622,-12.39 -27.934,-18.583 -45.931,-18.583 -17.999,0 -33.309,6.193 -45.932,18.583 -12.622,12.387 -18.934,27.524 -18.934,45.405 0,17.883 6.312,33.017 18.934,45.406" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,1504.6337,123.45627)"
id="g44"><path
id="path46"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -6.844,-66.786 c 0,-0.236 -0.118,-1.228 -0.352,-2.98 -0.235,-1.754 -0.526,-3.506 -0.876,-5.26 H 38.385 V 0 Z m -21.036,24.543 h 83.089 l 0.876,-0.876 v -98.693 h 13.149 l 0.876,-0.876 v -55.601 l -0.876,-0.876 H 53.287 l -0.877,0.876 v 31.934 h -78.889 v -31.934 l -0.877,-0.876 h -22.79 l -0.877,0.876 v 55.601 l 0.877,0.876 h 15.076 c 1.052,2.104 2.279,6.193 3.682,12.272 l 9.474,86.421 z" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,223.65853,66.988804)"
id="g172"><path
id="path174"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 13.018,-5.541 25.524,-12.206 37.384,-19.879 l 2.052,19.527 C 26.921,7.161 13.747,13.687 0,19.08 -13.796,13.667 -27.015,7.114 -39.569,-0.433 l 2.052,-19.533 C -25.618,-12.256 -13.067,-5.562 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,297.69626,294.9628)"
id="g176"><path
id="path178"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -13.756,-3.155 -27.812,-5.121 -41.983,-5.889 l 9.809,-16.991 c 14.84,1.298 29.336,3.794 43.373,7.436 7.889,12.311 14.762,25.332 20.554,38.923 L 18.612,38.074 C 13.521,24.892 7.301,12.143 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,343.41892,154.09427)"
id="g180"><path
id="path182"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -1.23,-14.123 -3.684,-28.089 -7.295,-41.733 l 19.221,4.085 c 3.227,14.124 5.34,28.673 6.213,43.557 C 8.787,17.273 -1.509,27.827 -12.6,37.491 l -17.923,-7.98 C -19.559,20.585 -9.334,10.716 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,103.89026,154.0924)"
id="g184"><path
id="path186"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 9.318,10.697 19.524,20.551 30.469,29.466 L 12.548,37.444 C 1.477,27.793 -8.801,17.255 -18.138,5.909 c 0.872,-14.864 2.98,-29.393 6.2,-43.499 L 7.28,-41.675 C 3.677,-28.05 1.229,-14.103 0,0" /></g><g
transform="matrix(1.3333333,0,0,-1.3333333,149.6168,294.96213)"
id="g188"><path
id="path190"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -7.321,12.177 -13.554,24.964 -18.653,38.184 L -31.799,23.585 c 5.804,-13.628 12.69,-26.685 20.6,-39.028 14.036,-3.642 28.53,-6.139 43.367,-7.438 l 9.81,16.991 C 27.808,-5.121 13.756,-3.154 0,0" /></g></svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

51
public/img/logo2.svg

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 377.95334 377.95334"
height="377.95334"
width="377.95334"
xml:space="preserve"
id="svg2"
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,283.465 H 283.465 V 0 H 0 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,0,377.95333)"
id="g10"><g
id="g12"><g
clip-path="url(#clipPath18)"
id="g14"><g
transform="translate(141.7347,229.1228)"
id="g20"><path
id="path22"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 13.018,-5.541 25.524,-12.205 37.383,-19.879 l 2.053,19.527 C 26.921,7.161 13.747,13.687 0,19.08 -13.796,13.667 -27.015,7.114 -39.57,-0.433 l 2.053,-19.533 C -25.619,-12.256 -13.067,-5.562 0,0" /></g><g
transform="translate(197.2625,58.1423)"
id="g24"><path
id="path26"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -13.756,-3.155 -27.811,-5.12 -41.983,-5.889 l 9.81,-16.991 c 14.84,1.298 29.335,3.794 43.372,7.436 7.89,12.312 14.762,25.332 20.555,38.923 L 18.613,38.074 C 13.521,24.892 7.301,12.143 0,0" /></g><g
transform="translate(231.5555,163.7942)"
id="g28"><path
id="path30"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -1.231,-14.124 -3.684,-28.09 -7.295,-41.733 l 19.22,4.085 c 3.228,14.124 5.34,28.673 6.213,43.556 -9.351,11.365 -19.647,21.919 -30.739,31.583 l -17.923,-7.98 C -19.559,20.584 -9.334,10.716 0,0" /></g><g
transform="translate(51.909,163.7956)"
id="g32"><path
id="path34"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 9.318,10.697 19.524,20.551 30.468,29.465 L 12.547,37.444 C 1.476,27.793 -8.802,17.255 -18.138,5.908 c 0.872,-14.863 2.979,-29.393 6.2,-43.498 L 7.28,-41.675 C 3.676,-28.05 1.229,-14.103 0,0" /></g><g
transform="translate(86.2034,58.1433)"
id="g36"><path
id="path38"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -7.321,12.177 -13.553,24.963 -18.654,38.184 L -31.798,23.585 c 5.802,-13.629 12.689,-26.686 20.599,-39.029 14.035,-3.641 28.53,-6.139 43.366,-7.437 L 41.978,-5.89 C 27.808,-5.121 13.756,-3.155 0,0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

1
routers/api/v1/admin/org.go

@ -62,6 +62,7 @@ func CreateOrg(ctx *context.APIContext) {
Description: form.Description, Description: form.Description,
Website: form.Website, Website: form.Website,
Location: form.Location, Location: form.Location,
LocationCoordinate: form.LocationCoordinate,
IsActive: true, IsActive: true,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
Visibility: visibility, Visibility: visibility,

3
routers/api/v1/admin/user.go

@ -240,6 +240,9 @@ func EditUser(ctx *context.APIContext) {
if form.Location != nil { if form.Location != nil {
u.Location = *form.Location u.Location = *form.Location
} }
if form.LocationCoordinate != nil {
u.LocationCoordinate = *form.LocationCoordinate
}
if form.Description != nil { if form.Description != nil {
u.Description = *form.Description u.Description = *form.Description
} }

2
routers/api/v1/org/org.go

@ -267,6 +267,7 @@ func Create(ctx *context.APIContext) {
Description: form.Description, Description: form.Description,
Website: form.Website, Website: form.Website,
Location: form.Location, Location: form.Location,
LocationCoordinate: form.LocationCoordinate,
IsActive: true, IsActive: true,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
Visibility: visibility, Visibility: visibility,
@ -340,6 +341,7 @@ func Edit(ctx *context.APIContext) {
org.Description = form.Description org.Description = form.Description
org.Website = form.Website org.Website = form.Website
org.Location = form.Location org.Location = form.Location
org.LocationCoordinate = form.LocationCoordinate
if form.Visibility != "" { if form.Visibility != "" {
org.Visibility = api.VisibilityModes[form.Visibility] org.Visibility = api.VisibilityModes[form.Visibility]
} }

2
routers/api/v1/repo/repo.go

@ -776,6 +776,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
EnableTimetracker: opts.InternalTracker.EnableTimeTracker, EnableTimetracker: opts.InternalTracker.EnableTimeTracker,
AllowOnlyContributorsToTrackTime: opts.InternalTracker.AllowOnlyContributorsToTrackTime, AllowOnlyContributorsToTrackTime: opts.InternalTracker.AllowOnlyContributorsToTrackTime,
EnableDependencies: opts.InternalTracker.EnableIssueDependencies, EnableDependencies: opts.InternalTracker.EnableIssueDependencies,
EnableParents: opts.InternalTracker.EnableIssueParents,
} }
} else if unit, err := repo.GetUnit(unit_model.TypeIssues); err != nil { } else if unit, err := repo.GetUnit(unit_model.TypeIssues); err != nil {
// Unit type doesn't exist so we make a new config file with default values // Unit type doesn't exist so we make a new config file with default values
@ -783,6 +784,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
EnableTimetracker: true, EnableTimetracker: true,
AllowOnlyContributorsToTrackTime: true, AllowOnlyContributorsToTrackTime: true,
EnableDependencies: true, EnableDependencies: true,
EnableParents: true,
} }
} else { } else {
config = unit.IssuesConfig() config = unit.IssuesConfig()

3
routers/api/v1/user/settings.go

@ -57,6 +57,9 @@ func UpdateUserSettings(ctx *context.APIContext) {
if form.Location != nil { if form.Location != nil {
ctx.User.Location = *form.Location ctx.User.Location = *form.Location
} }
if form.LocationCoordinate != nil {
ctx.User.LocationCoordinate = *form.LocationCoordinate
}
if form.Language != nil { if form.Language != nil {
ctx.User.Language = *form.Language ctx.User.Language = *form.Language
} }

2
routers/web/admin/users.go

@ -371,7 +371,9 @@ func EditUserPost(ctx *context.Context) {
emailChanged := !strings.EqualFold(u.Email, form.Email) emailChanged := !strings.EqualFold(u.Email, form.Email)
u.Email = form.Email u.Email = form.Email
u.Website = form.Website u.Website = form.Website
u.Description = form.Description
u.Location = form.Location u.Location = form.Location
u.LocationCoordinate = form.LocationCoordinate
u.MaxRepoCreation = form.MaxRepoCreation u.MaxRepoCreation = form.MaxRepoCreation
u.IsActive = form.Active u.IsActive = form.Active
u.IsAdmin = form.Admin u.IsAdmin = form.Admin

127
routers/web/map/umap.go

@ -0,0 +1,127 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package umap
import (
"bytes"
"net/http"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
)
const (
// tplExploreUsers map page template
tplExploreUsers base.TplName = "map/umap"
)
// UserSearchDefaultSortType is the default sort type for user search
const UserSearchDefaultSortType = "alphabetically"
var (
nullByte = []byte{0x00}
)
func isKeywordValid(keyword string) bool {
return !bytes.Contains([]byte(keyword), nullByte)
}
// RenderUserSearch render user search page
func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName base.TplName) {
opts.PageSize = 8 * 1024 // Get all users
opts.Page = ctx.FormInt("page")
if opts.Page <= 1 {
opts.Page = 1
}
var (
users []*user_model.User
count int64
err error
orderBy db.SearchOrderBy
)
// we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
ctx.Data["SortType"] = ctx.FormString("sort")
switch ctx.FormString("sort") {
case "newest":
orderBy = "`user`.id DESC"
case "oldest":
orderBy = "`user`.id ASC"
case "recentupdate":
orderBy = "`user`.updated_unix DESC"
case "leastupdate":
orderBy = "`user`.updated_unix ASC"
case "reversealphabetically":
orderBy = "`user`.name DESC"
case UserSearchDefaultSortType: // "alphabetically"
default:
orderBy = "`user`.name ASC"
ctx.Data["SortType"] = UserSearchDefaultSortType
}
opts.Keyword = ctx.FormTrim("q")
opts.OrderBy = orderBy
if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
users, count, err = user_model.SearchUsers(opts)
if err != nil {
ctx.ServerError("SearchUsers", err)
return
}
}
for i := range users {
if len(users[i].Description) != 0 {
content, err := markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.Repo.RepoLink,
Metas: map[string]string{"mode": "document"},
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
}, users[i].Description)
if err != nil {
ctx.ServerError("RenderString", err)
return
}
users[i].Description = content
}
}
ctx.Data["Keyword"] = opts.Keyword
ctx.Data["Total"] = count
ctx.Data["Users"] = users
ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus()
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.HTML(http.StatusOK, tplName)
}
// Users render explore users page
func UsersMap(ctx *context.Context) {
if setting.Service.Explore.DisableUsersPage {
ctx.Redirect(setting.AppSubURL + "/map/umap")
return
}
ctx.Data["Title"] = ctx.Tr("map")
ctx.Data["PageIsMap"] = true
ctx.Data["PageIsMapUsers"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
RenderUserSearch(ctx, &user_model.SearchUserOptions{
Actor: ctx.User,
Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
IsActive: util.OptionalBoolTrue,
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
}, tplExploreUsers)
}

1
routers/web/org/setting.go

@ -100,6 +100,7 @@ func SettingsPost(ctx *context.Context) {
org.Description = form.Description org.Description = form.Description
org.Website = form.Website org.Website = form.Website
org.Location = form.Location org.Location = form.Location
org.LocationCoordinate = form.LocationCoordinate
org.RepoAdminChangeTeamAccess = form.RepoAdminChangeTeamAccess org.RepoAdminChangeTeamAccess = form.RepoAdminChangeTeamAccess
visibilityChanged := form.Visibility != org.Visibility visibilityChanged := form.Visibility != org.Visibility

78
routers/web/repo/issue.go

@ -47,6 +47,7 @@ const (
tplAttachment base.TplName = "repo/issue/view_content/attachments" tplAttachment base.TplName = "repo/issue/view_content/attachments"
tplIssues base.TplName = "repo/issue/list" tplIssues base.TplName = "repo/issue/list"
tplIssuesTree base.TplName = "repo/issue/tree"
tplIssueNew base.TplName = "repo/issue/new" tplIssueNew base.TplName = "repo/issue/new"
tplIssueChoose base.TplName = "repo/issue/choose" tplIssueChoose base.TplName = "repo/issue/choose"
tplIssueView base.TplName = "repo/issue/view" tplIssueView base.TplName = "repo/issue/view"
@ -114,7 +115,7 @@ func MustAllowPulls(ctx *context.Context) {
} }
} }
func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption util.OptionalBool) { func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption util.OptionalBool, page_size int) {
var err error var err error
viewType := ctx.FormString("type") viewType := ctx.FormString("type")
sortType := ctx.FormString("sort") sortType := ctx.FormString("sort")
@ -210,7 +211,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
} else { } else {
total = int(issueStats.ClosedCount) total = int(issueStats.ClosedCount)
} }
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) pager := context.NewPagination(total, page_size, page, 5)
var mileIDs []int64 var mileIDs []int64
if milestoneID > 0 { if milestoneID > 0 {
@ -224,7 +225,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
issues, err = models.Issues(&models.IssuesOptions{ issues, err = models.Issues(&models.IssuesOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: pager.Paginater.Current(), Page: pager.Paginater.Current(),
PageSize: setting.UI.IssuePagingNum, PageSize: page_size,
}, },
RepoID: repo.ID, RepoID: repo.ID,
AssigneeID: assigneeID, AssigneeID: assigneeID,
@ -391,7 +392,7 @@ func Issues(ctx *context.Context) {
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0 ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
} }
issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), util.OptionalBoolOf(isPullList)) issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), util.OptionalBoolOf(isPullList), setting.UI.IssuePagingNum)
if ctx.Written() { if ctx.Written() {
return return
} }
@ -412,6 +413,47 @@ func Issues(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplIssues) ctx.HTML(http.StatusOK, tplIssues)
} }
// Issues Tree render issues page
func IssuesTree(ctx *context.Context) {
isPullList := ctx.Params(":type") == "pulls"
if isPullList {
MustAllowPulls(ctx)
if ctx.Written() {
return
}
ctx.Data["Title"] = ctx.Tr("repo.pulls")
ctx.Data["PageIsPullList"] = true
} else {
MustEnableIssues(ctx)
if ctx.Written() {
return
}
ctx.Data["Title"] = ctx.Tr("repo.issues_tree")
ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
}
issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), util.OptionalBoolOf(isPullList), 32*1024)
if ctx.Written() {
return
}
var err error
// Get milestones
ctx.Data["Milestones"], _, err = models.GetMilestones(models.GetMilestonesOption{
RepoID: ctx.Repo.Repository.ID,
State: api.StateType(ctx.FormString("state")),
})
if err != nil {
ctx.ServerError("GetAllRepoMilestones", err)
return
}
ctx.Data["CanWriteIssuesOrPulls"] = ctx.Repo.CanWriteIssuesOrPulls(isPullList)
ctx.HTML(http.StatusOK, tplIssuesTree)
}
// RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository // RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository
func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.Repository) { func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.Repository) {
var err error var err error
@ -701,6 +743,9 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
// Contains true if the user can create issue dependencies // Contains true if the user can create issue dependencies
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, isPull) ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, isPull)
// Contains true if the user can create issue parents
ctx.Data["CanCreateIssueParents"] = ctx.Repo.CanCreateIssueParents(ctx.User, isPull)
return labels return labels
} }
@ -1324,6 +1369,12 @@ func ViewIssue(ctx *context.Context) {
// check if dependencies can be created across repositories // check if dependencies can be created across repositories
ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies
// Check if the user can use the parents
ctx.Data["CanCreateIssueParents"] = ctx.Repo.CanCreateIssueParents(ctx.User, issue.IsPull)
// check if parents can be created across repositories
ctx.Data["AllowCrossRepositoryParents"] = setting.Service.AllowCrossRepositoryParents
if issue.ShowRole, err = roleDescriptor(repo, issue.Poster, issue); err != nil { if issue.ShowRole, err = roleDescriptor(repo, issue.Poster, issue); err != nil {
ctx.ServerError("roleDescriptor", err) ctx.ServerError("roleDescriptor", err)
return return
@ -1422,6 +1473,13 @@ func ViewIssue(ctx *context.Context) {
return return
} }
} }
} else if comment.Type == models.CommentTypeRemoveParent || comment.Type == models.CommentTypeAddParent {
if err = comment.LoadParentIssueDetails(); err != nil {
if !models.IsErrIssueNotExist(err) {
ctx.ServerError("LoadParentIssueDetails", err)
return
}
}
} else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview || comment.Type == models.CommentTypeDismissReview { } else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview || comment.Type == models.CommentTypeDismissReview {
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.Repo.RepoLink, URLPrefix: ctx.Repo.RepoLink,
@ -1650,6 +1708,18 @@ func ViewIssue(ctx *context.Context) {
return return
} }
// Get Parents
ctx.Data["BlockedByParents"], err = issue.BlockedByParents()
if err != nil {
ctx.ServerError("BlockedByParents", err)
return
}
ctx.Data["BlockingParents"], err = issue.BlockingParents()
if err != nil {
ctx.ServerError("BlockingParents", err)
return
}
ctx.Data["Participants"] = participants ctx.Data["Participants"] = participants
ctx.Data["NumParticipants"] = len(participants) ctx.Data["NumParticipants"] = len(participants)
ctx.Data["Issue"] = issue ctx.Data["Issue"] = issue

129
routers/web/repo/issue_parent.go

@ -0,0 +1,129 @@
// Copyright 2018-2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"net/http"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
)
// AddParent adds new parents
func AddParent(ctx *context.Context) {
issueIndex := ctx.ParamsInt64("index")
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
if err != nil {
ctx.ServerError("GetIssueByIndex", err)
return
}
// Check if the Repo is allowed to have parents
if !ctx.Repo.CanCreateIssueParents(ctx.User, issue.IsPull) {
ctx.Error(http.StatusForbidden, "CanCreateIssueParents")
return
}
parentID := ctx.FormInt64("newParent")
if err = issue.LoadRepo(); err != nil {
ctx.ServerError("LoadRepo", err)
return
}
// Redirect
defer ctx.Redirect(issue.HTMLURL(), http.StatusSeeOther)
// Parent
parent, err := models.GetIssueByID(parentID)
if err != nil {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_dep_issue_not_exist"))
return
}
// Check if both issues are in the same repo if cross repository parents is not enabled
if issue.RepoID != parent.RepoID && !setting.Service.AllowCrossRepositoryParents {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_dep_not_same_repo"))
return
}
// Check if issue and parent is the same
if parent.ID == issue.ID {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_same_issue"))
return
}
err = models.CreateIssueParent(ctx.User, issue, parent)
if err != nil {
if models.IsErrParentExists(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_dep_exists"))
return
} else if models.IsErrCircularParent(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_cannot_create_circular"))
return
} else {
ctx.ServerError("CreateOrUpdateIssueParent", err)
return
}
}
}
// RemoveParent removes the parent
func RemoveParent(ctx *context.Context) {
issueIndex := ctx.ParamsInt64("index")
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
if err != nil {
ctx.ServerError("GetIssueByIndex", err)
return
}
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueParents(ctx.User, issue.IsPull) {
ctx.Error(http.StatusForbidden, "CanCreateIssueParents")
return
}
parentID := ctx.FormInt64("removeParentID")
if err = issue.LoadRepo(); err != nil {
ctx.ServerError("LoadRepo", err)
return
}
// Parent Type
parentTypeStr := ctx.Req.PostForm.Get("parentType")
var parentType models.ParentType
switch parentTypeStr {
case "father":
parentType = models.ParentTypeFather
case "child":
parentType = models.ParentTypeChild
default:
ctx.Error(http.StatusBadRequest, "GetParentType")
return
}
// Parent
parent, err := models.GetIssueByID(parentID)
if err != nil {
ctx.ServerError("GetIssueByID", err)
return
}
if err = models.RemoveIssueParent(ctx.User, issue, parent, parentType); err != nil {
if models.IsErrParentNotExists(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.parent.add_error_dep_not_exist"))
return
}
ctx.ServerError("RemoveIssueParent", err)
return
}
// Redirect
ctx.Redirect(issue.HTMLURL(), http.StatusSeeOther)
}

2
routers/web/repo/milestone.go

@ -289,7 +289,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
ctx.Data["Title"] = milestone.Name ctx.Data["Title"] = milestone.Name
ctx.Data["Milestone"] = milestone ctx.Data["Milestone"] = milestone
issues(ctx, milestoneID, 0, util.OptionalBoolNone) issues(ctx, milestoneID, 0, util.OptionalBoolNone, setting.UI.IssuePagingNum)
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0 ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWriteIssuesOrPulls(false) ctx.Data["CanWriteIssues"] = ctx.Repo.CanWriteIssuesOrPulls(false)

1
routers/web/repo/setting.go

@ -434,6 +434,7 @@ func SettingsPost(ctx *context.Context) {
EnableTimetracker: form.EnableTimetracker, EnableTimetracker: form.EnableTimetracker,
AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
EnableDependencies: form.EnableIssueDependencies, EnableDependencies: form.EnableIssueDependencies,
EnableParents: form.EnableIssueParents,
}, },
}) })
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)

10
routers/web/repo/view.go

@ -697,6 +697,16 @@ func Home(ctx *context.Context) {
return return
} }
Wiki(ctx)
}
// Code render repository page
func Code(ctx *context.Context) {
checkHomeCodeViewable(ctx)
if ctx.Written() {
return
}
renderCode(ctx) renderCode(ctx)
} }

1
routers/web/user/setting/profile.go

@ -119,6 +119,7 @@ func ProfilePost(ctx *context.Context) {
ctx.User.KeepEmailPrivate = form.KeepEmailPrivate ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
ctx.User.Website = form.Website ctx.User.Website = form.Website
ctx.User.Location = form.Location ctx.User.Location = form.Location
ctx.User.LocationCoordinate = form.LocationCoordinate
ctx.User.Description = form.Description ctx.User.Description = form.Description
ctx.User.KeepActivityPrivate = form.KeepActivityPrivate ctx.User.KeepActivityPrivate = form.KeepActivityPrivate
ctx.User.Visibility = form.Visibility ctx.User.Visibility = form.Visibility

18
routers/web/web.go

@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/routers/web/dev" "code.gitea.io/gitea/routers/web/dev"
"code.gitea.io/gitea/routers/web/events" "code.gitea.io/gitea/routers/web/events"
"code.gitea.io/gitea/routers/web/explore" "code.gitea.io/gitea/routers/web/explore"
"code.gitea.io/gitea/routers/web/map"
"code.gitea.io/gitea/routers/web/org" "code.gitea.io/gitea/routers/web/org"
"code.gitea.io/gitea/routers/web/repo" "code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/routers/web/user" "code.gitea.io/gitea/routers/web/user"
@ -254,6 +255,9 @@ func RegisterRoutes(m *web.Route) {
m.Get("/organizations", explore.Organizations) m.Get("/organizations", explore.Organizations)
m.Get("/code", explore.Code) m.Get("/code", explore.Code)
}, ignExploreSignIn) }, ignExploreSignIn)
m.Group("/map", func() {
m.Get("/umap", umap.UsersMap)
}, ignExploreSignIn)
m.Get("/issues", reqSignIn, user.Issues) m.Get("/issues", reqSignIn, user.Issues)
m.Get("/pulls", reqSignIn, user.Pulls) m.Get("/pulls", reqSignIn, user.Pulls)
m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones)
@ -737,6 +741,10 @@ func RegisterRoutes(m *web.Route) {
m.Post("/add", repo.AddDependency) m.Post("/add", repo.AddDependency)
m.Post("/delete", repo.RemoveDependency) m.Post("/delete", repo.RemoveDependency)
}) })
m.Group("/parent", func() {
m.Post("/add", repo.AddParent)
m.Post("/delete", repo.RemoveParent)
})
m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(forms.CreateCommentForm{}), repo.NewComment) m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(forms.CreateCommentForm{}), repo.NewComment)
m.Group("/times", func() { m.Group("/times", func() {
m.Post("/add", bindIgnErr(forms.AddTimeManuallyForm{}), repo.AddTimeManually) m.Post("/add", bindIgnErr(forms.AddTimeManuallyForm{}), repo.AddTimeManually)
@ -877,6 +885,7 @@ func RegisterRoutes(m *web.Route) {
m.Group("/{username}/{reponame}", func() { m.Group("/{username}/{reponame}", func() {
m.Group("", func() { m.Group("", func() {
m.Get("/{type:issues|pulls}", repo.Issues) m.Get("/{type:issues|pulls}", repo.Issues)
m.Get("/issues_tree", repo.IssuesTree)
m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue) m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue)
m.Group("/{type:issues|pulls}/{index}/content-history", func() { m.Group("/{type:issues|pulls}/{index}/content-history", func() {
m.Get("/overview", repo.GetContentHistoryOverview) m.Get("/overview", repo.GetContentHistoryOverview)
@ -1033,11 +1042,11 @@ func RegisterRoutes(m *web.Route) {
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
m.Group("/src", func() { m.Group("/src", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Code)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Code)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Code)
// "/*" route is deprecated, and kept for backward compatibility // "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Code)
}, repo.SetEditorconfigIfExists) }, repo.SetEditorconfigIfExists)
m.Group("", func() { m.Group("", func() {
@ -1058,6 +1067,7 @@ func RegisterRoutes(m *web.Route) {
m.Group("/{username}", func() { m.Group("/{username}", func() {
m.Group("/{reponame}", func() { m.Group("/{reponame}", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.Home) m.Get("", repo.SetEditorconfigIfExists, repo.Home)
m.Get("/code", repo.SetEditorconfigIfExists, repo.Code)
}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes()) }, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
m.Group("/{reponame}", func() { m.Group("/{reponame}", func() {

2
services/forms/admin.go

@ -41,7 +41,9 @@ type AdminEditUserForm struct {
Email string `binding:"Required;Email;MaxSize(254)"` Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"MaxSize(255)"` Password string `binding:"MaxSize(255)"`
Website string `binding:"ValidUrl;MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"`
Description string `binding:"MaxSize(1024)"`
Location string `binding:"MaxSize(50)"` Location string `binding:"MaxSize(50)"`
LocationCoordinate string `binding:"MaxSize(255)"`
MaxRepoCreation int MaxRepoCreation int
Active bool Active bool
Admin bool Admin bool

3
services/forms/org.go

@ -39,9 +39,10 @@ func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding
type UpdateOrgSettingForm struct { type UpdateOrgSettingForm struct {
Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"` Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
FullName string `binding:"MaxSize(100)"` FullName string `binding:"MaxSize(100)"`
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(1024)"`
Website string `binding:"ValidUrl;MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"` Location string `binding:"MaxSize(50)"`
LocationCoordinate string `binding:"MaxSize(255)"`
Visibility structs.VisibleType Visibility structs.VisibleType
MaxRepoCreation int MaxRepoCreation int
RepoAdminChangeTeamAccess bool RepoAdminChangeTeamAccess bool

3
services/forms/repo_form.go

@ -74,7 +74,7 @@ type MigrateRepoForm struct {
LFS bool `json:"lfs"` LFS bool `json:"lfs"`
LFSEndpoint string `json:"lfs_endpoint"` LFSEndpoint string `json:"lfs_endpoint"`
Private bool `json:"private"` Private bool `json:"private"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(1024)"`
Wiki bool `json:"wiki"` Wiki bool `json:"wiki"`
Milestones bool `json:"milestones"` Milestones bool `json:"milestones"`
Labels bool `json:"labels"` Labels bool `json:"labels"`
@ -155,6 +155,7 @@ type RepoSettingForm struct {
EnableTimetracker bool EnableTimetracker bool
AllowOnlyContributorsToTrackTime bool AllowOnlyContributorsToTrackTime bool
EnableIssueDependencies bool EnableIssueDependencies bool
EnableIssueParents bool
IsArchived bool IsArchived bool
// Signing Settings // Signing Settings

5
services/forms/user_form.go

@ -245,7 +245,8 @@ type UpdateProfileForm struct {
KeepEmailPrivate bool KeepEmailPrivate bool
Website string `binding:"ValidSiteUrl;MaxSize(255)"` Website string `binding:"ValidSiteUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"` Location string `binding:"MaxSize(50)"`
Description string `binding:"MaxSize(255)"` LocationCoordinate string `binding:"MaxSize(255)"`
Description string `binding:"MaxSize(1024)"`
Visibility structs.VisibleType Visibility structs.VisibleType
KeepActivityPrivate bool KeepActivityPrivate bool
} }
@ -273,7 +274,7 @@ const (
AvatarByMail string = "bymail" AvatarByMail string = "bymail"
) )
// AvatarForm form for changing avatar // AvatarForm form for changing avDescriptionatar
type AvatarForm struct { type AvatarForm struct {
Source string Source string
Avatar *multipart.FileHeader Avatar *multipart.FileHeader

2
templates/admin/config.tmpl

@ -186,6 +186,8 @@
<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd> <dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd>
<dt>{{.i18n.Tr "admin.config.default_enable_dependencies"}}</dt> <dt>{{.i18n.Tr "admin.config.default_enable_dependencies"}}</dt>
<dd>{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> <dd>{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
<dt>{{.i18n.Tr "admin.config.default_enable_parents"}}</dt>
<dd>{{if .Service.DefaultEnableParents}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
<div class="ui divider"></div> <div class="ui divider"></div>
<dt>{{.i18n.Tr "admin.config.active_code_lives"}}</dt> <dt>{{.i18n.Tr "admin.config.active_code_lives"}}</dt>
<dd>{{.Service.ActiveCodeLives}} {{.i18n.Tr "tool.raw_minutes"}}</dd> <dd>{{.Service.ActiveCodeLives}} {{.i18n.Tr "tool.raw_minutes"}}</dd>

8
templates/admin/user/edit.tmpl

@ -73,6 +73,10 @@
<input id="password" name="password" type="password" autocomplete="new-password"> <input id="password" name="password" type="password" autocomplete="new-password">
<p class="help">{{.i18n.Tr "admin.users.password_helper"}}</p> <p class="help">{{.i18n.Tr "admin.users.password_helper"}}</p>
</div> </div>
<div class="field {{if .Err_Description}}error{{end}}">
<label for="description">{{$.i18n.Tr "user.user_bio"}}</label>
<textarea id="description" name="description" rows="4" placeholder="{{.i18n.Tr "settings.biography_placeholder"}}">{{.User.Description}}</textarea>
</div>
<div class="field {{if .Err_Website}}error{{end}}"> <div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{.i18n.Tr "settings.website"}}</label> <label for="website">{{.i18n.Tr "settings.website"}}</label>
<input id="website" name="website" type="url" value="{{.User.Website}}" placeholder="e.g. http://mydomain.com or https://mydomain.com"> <input id="website" name="website" type="url" value="{{.User.Website}}" placeholder="e.g. http://mydomain.com or https://mydomain.com">
@ -81,6 +85,10 @@
<label for="location">{{.i18n.Tr "settings.location"}}</label> <label for="location">{{.i18n.Tr "settings.location"}}</label>
<input id="location" name="location" value="{{.User.Location}}"> <input id="location" name="location" value="{{.User.Location}}">
</div> </div>
<div class="field {{if .LocationCoordinate}}error{{end}}">
<label for="location_coordinate">{{.i18n.Tr "settings.locationcoordinate"}}</label>
<input id="location_coordinate" name="location_coordinate" value="{{.User.LocationCoordinate}}" placeholder="55.4, 37.3">
</div>
<div class="ui divider"></div> <div class="ui divider"></div>

1
templates/base/head_navbar.tmpl

@ -21,6 +21,7 @@
{{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}} {{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}}
{{end}} {{end}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "explore"}}</a> <a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "explore"}}</a>
<a class="item {{if .PageIsMap}}active{{end}}" href="{{AppSubUrl}}/map/umap">{{.i18n.Tr "map"}}</a>
{{else if .IsLandingPageOrganizations}} {{else if .IsLandingPageOrganizations}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore"}}</a> <a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore"}}</a>
{{else}} {{else}}

2
templates/explore/organizations.tmpl

@ -17,7 +17,7 @@
</span> </span>
<div class="description"> <div class="description">
{{if .Location}} {{if .Location}}
{{svg "octicon-location"}} {{.Location}} {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}})
{{end}} {{end}}
{{if and .Website}} {{if and .Website}}
{{svg "octicon-link"}} {{svg "octicon-link"}}

2
templates/explore/users.tmpl

@ -12,7 +12,7 @@
<span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span> <span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span>
<div class="description"> <div class="description">
{{if .Location}} {{if .Location}}
{{svg "octicon-location"}} {{.Location}} {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}})
{{end}} {{end}}
{{if and $.ShowUserEmail .Email $.IsSigned (not .KeepEmailPrivate)}} {{if and $.ShowUserEmail .Email $.IsSigned (not .KeepEmailPrivate)}}
{{svg "octicon-mail"}} {{svg "octicon-mail"}}

18
templates/map/navbar.tmpl

@ -0,0 +1,18 @@
<div class="ui secondary pointing tabular top attached borderless stackable menu new-menu navbar">
<a class="{{if .PageIsMapRepositories}}active{{end}} item" href="{{AppSubUrl}}/map/rmap">
{{svg "octicon-repo"}} {{.i18n.Tr "explore.repos"}}
</a>
{{if not .UsersIsDisabled}}
<a class="{{if .PageIsMapUsers}}active{{end}} item" href="{{AppSubUrl}}/map/umap">
{{svg "octicon-person"}} {{.i18n.Tr "explore.users"}}
</a>
{{end}}
<a class="{{if .PageIsMapOrganizations}}active{{end}} item" href="{{AppSubUrl}}/map/orgmap">
{{svg "octicon-organization"}} {{.i18n.Tr "explore.organizations"}}
</a>
{{if .IsRepoIndexerEnabled}}
<a class="{{if .PageIsMapCode}}active{{end}} item" href="{{AppSubUrl}}/map/codemap">
{{svg "octicon-code"}} {{.i18n.Tr "explore.code"}}
</a>
{{end}}
</div>

2862
templates/map/umap.tmpl

File diff suppressed because it is too large Load Diff

2
templates/org/home.tmpl

@ -12,7 +12,7 @@
</div> </div>
{{if $.RenderedDescription}}<p class="render-content markup">{{$.RenderedDescription|Str2html}}</p>{{end}} {{if $.RenderedDescription}}<p class="render-content markup">{{$.RenderedDescription|Str2html}}</p>{{end}}
<div class="text grey meta"> <div class="text grey meta">
{{if .Org.Location}}<div class="item">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}} {{if .Org.Location}}<div class="item">{{svg "octicon-location"}} <span>{{.Org.Location}}</span> <span>({{.Org.LocationCoordinate}})</span></div>{{end}}
{{if .Org.Website}}<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}} {{if .Org.Website}}<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
</div> </div>
</div> </div>

5
templates/org/settings/options.tmpl

@ -36,6 +36,11 @@
<input id="location" name="location" value="{{.Org.Location}}"> <input id="location" name="location" value="{{.Org.Location}}">
</div> </div>
<div class="field">
<label for="location_coordinate">{{.i18n.Tr "org.settings.locationcoordinate"}}</label>
<input id="location_coordinate" name="location_coordinate" value="{{.Org.LocationCoordinate}}" placeholder="55.4, 37.3">
</div>
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="field" id="visibility_box"> <div class="field" id="visibility_box">
<label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label> <label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label>

43
templates/repo/header.tmpl

@ -2,7 +2,7 @@
{{with .Repository}} {{with .Repository}}
<div class="ui container"> <div class="ui container">
<div class="repo-header"> <div class="repo-header">
<div class="repo-title-wrap df fc"> <div id="repo-title-in-header" class="repo-title-wrap df fc">
<div class="repo-title"> <div class="repo-title">
{{$avatar := (repoAvatar . 32 "mr-3")}} {{$avatar := (repoAvatar . 32 "mr-3")}}
{{if $avatar}} {{if $avatar}}
@ -147,12 +147,12 @@
<div class="ui tabs container"> <div class="ui tabs container">
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}} {{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
<div class="ui tabular stackable menu navbar"> <div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}} {{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{end}}"> <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-code"}} {{.i18n.Tr "repo.code"}} {{svg "octicon-book"}} {{.i18n.Tr "repo.wiki"}}
</a> </a>
{{end}} {{end}}
{{if .Permission.CanRead $.UnitTypeIssues}} {{if .Permission.CanRead $.UnitTypeIssues}}
<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
{{svg "octicon-issue-opened"}} {{.i18n.Tr "repo.issues"}} {{svg "octicon-issue-opened"}} {{.i18n.Tr "repo.issues"}}
@ -168,15 +168,6 @@
</a> </a>
{{end}} {{end}}
{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
{{svg "octicon-git-pull-request"}} {{.i18n.Tr "repo.pulls"}}
{{if .Repository.NumOpenPulls}}
<span class="ui blue small label">{{CountFmt .Repository.NumOpenPulls}}</span>
{{end}}
</a>
{{end}}
{{ if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}} {{ if and (not .UnitProjectsGlobalDisabled) (.Permission.CanRead $.UnitTypeProjects)}}
<a href="{{.RepoLink}}/projects" class="{{ if .IsProjectsPage }}active{{end}} item"> <a href="{{.RepoLink}}/projects" class="{{ if .IsProjectsPage }}active{{end}} item">
{{svg "octicon-project"}} {{.i18n.Tr "repo.project_board"}} {{svg "octicon-project"}} {{.i18n.Tr "repo.project_board"}}
@ -185,6 +176,22 @@
{{end}} {{end}}
</a> </a>
{{ end }} {{ end }}
{{if .Permission.CanRead $.UnitTypeCode}}
<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL}}{{else}}/code{{end}}">
{{svg "octicon-code"}} {{.i18n.Tr "repo.code"}}
</a>
{{end}}
{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
{{svg "octicon-git-pull-request"}} {{.i18n.Tr "repo.pulls"}}
{{if .Repository.NumOpenPulls}}
<span class="ui blue small label">{{CountFmt .Repository.NumOpenPulls}}</span>
{{end}}
</a>
{{end}}
{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }} {{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> <a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
@ -195,12 +202,6 @@
</a> </a>
{{end}} {{end}}
{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-book"}} {{.i18n.Tr "repo.wiki"}}
</a>
{{end}}
{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}} {{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
<a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity"> <a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse"}} {{.i18n.Tr "repo.activity"}} {{svg "octicon-pulse"}} {{.i18n.Tr "repo.activity"}}

89
templates/repo/issue/issue_actions.tmpl

@ -0,0 +1,89 @@
<div id="issue-actions" class="ui stackable grid hide">
<div class="six wide column">
{{template "repo/issue/openclose" .}}
</div>
{{/* Ten wide does not cope well and makes the columns stack.
This seems to be related to jQuery's hide/show: in fact, switching
issue-actions and issue-filters and having this ten wide will show
this one correctly, but not the other one. */}}
<div class="nine wide right aligned right floated column">
<div class="ui secondary filter stackable menu">
{{if not .Repository.IsArchived}}
<!-- Action Button -->
{{if .IsShowClosed}}
<div class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.i18n.Tr "repo.issues.action_open"}}</div>
{{else}}
<div class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.i18n.Tr "repo.issues.action_close"}}</div>
{{end}}
<!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_label"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
{{range .Labels}}
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
{{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
</div>
{{end}}
</div>
</div>
<!-- Milestone -->
<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_milestone"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/milestone">
{{.i18n.Tr "repo.issues.action_milestone_no_select"}}
</div>
{{range .Milestones}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/milestone">
{{.Name}}
</div>
{{end}}
</div>
</div>
<!-- Projects -->
<div class="ui {{if not .Projects}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.project_board"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/projects">
{{.i18n.Tr "repo.issues.new.no_projects"}}
</div>
{{range .Projects}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects">
{{.Title}}
</div>
{{end}}
</div>
</div>
<!-- Assignees -->
<div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_assignee"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/assignee">
{{.i18n.Tr "repo.issues.action_assignee_no_select"}}
</div>
{{range .Assignees}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
{{avatar .}} {{.GetDisplayName}}
</div>
{{end}}
</div>
</div>
{{end}}
</div>
</div>
</div>

90
templates/repo/issue/issue_filters.tmpl

@ -0,0 +1,90 @@
<div id="issue-filters" class="ui stackable grid">
<div class="six wide column">
{{template "repo/issue/openclose" .}}
</div>
<div class="ten wide right aligned column">
<div class="ui secondary filter stackable menu labels">
<!-- Label -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item label-filter" style="margin-left: auto">
<span class="text">
{{.i18n.Tr "repo.issues.filter_label"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<span class="info">{{.i18n.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
{{range .Labels}}
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
{{end}}
</div>
</div>
<!-- Milestone -->
<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_milestone"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_milestone_no_select"}}</a>
{{range .Milestones}}
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&assignee={{$.AssigneeID}}">{{.Name}}</a>
{{end}}
</div>
</div>
<!-- Assignee -->
<div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_assignee"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_assginee_no_select"}}</a>
{{range .Assignees}}
<a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.ID}}">
{{avatar .}} {{.GetDisplayName}}
</a>
{{end}}
</div>
</div>
{{if .IsSigned}}
<!-- Type -->
<div class="ui dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_type"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a>
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a>
{{if .PageIsPullList}}
<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.review_requested"}}</a>
{{end}}
</div>
</div>
{{end}}
<!-- Sort -->
<div class="ui dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if or (eq .SortType "latest") (not .SortType)}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "mostcomment"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.mostcomment"}}</a>
<a class="{{if eq .SortType "leastcomment"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.leastcomment"}}</a>
<a class="{{if eq .SortType "nearduedate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.nearduedate"}}</a>
<a class="{{if eq .SortType "farduedate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.farduedate"}}</a>
</div>
</div>
</div>
</div>
</div>

204
templates/repo/issue/list.tmpl

@ -2,209 +2,11 @@
<div class="page-content repository"> <div class="page-content repository">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
<div class="ui three column stackable grid"> {{template "repo/issue/nav_search_and_new" .}}
<div class="column">
{{template "repo/issue/navbar" .}}
</div>
<div class="column center aligned">
{{template "repo/issue/search" .}}
</div>
{{if not .Repository.IsArchived}}
<div class="column right aligned">
{{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{.i18n.Tr "repo.pulls.new"}}</a>
{{end}}
</div>
{{else}}
{{if not .PageIsIssueList}}
<div class="column right aligned">
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{$.i18n.Tr "action.compare_commits_general"}}</a>
</div>
{{end}}
{{end}}
</div>
<div class="ui divider"></div> <div class="ui divider"></div>
<div id="issue-filters" class="ui stackable grid"> {{template "repo/issue/issue_filters" .}}
<div class="six wide column"> {{template "repo/issue/issue_actions" .}}
{{template "repo/issue/openclose" .}}
</div>
<div class="ten wide right aligned column">
<div class="ui secondary filter stackable menu labels">
<!-- Label -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item label-filter" style="margin-left: auto">
<span class="text">
{{.i18n.Tr "repo.issues.filter_label"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<span class="info">{{.i18n.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
{{range .Labels}}
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
{{end}}
</div>
</div>
<!-- Milestone -->
<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_milestone"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_milestone_no_select"}}</a>
{{range .Milestones}}
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&assignee={{$.AssigneeID}}">{{.Name}}</a>
{{end}}
</div>
</div>
<!-- Assignee -->
<div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_assignee"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_assginee_no_select"}}</a>
{{range .Assignees}}
<a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.ID}}">
{{avatar .}} {{.GetDisplayName}}
</a>
{{end}}
</div>
</div>
{{if .IsSigned}}
<!-- Type -->
<div class="ui dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_type"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a>
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a>
{{if .PageIsPullList}}
<a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.review_requested"}}</a>
{{end}}
</div>
</div>
{{end}}
<!-- Sort -->
<div class="ui dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<a class="{{if or (eq .SortType "latest") (not .SortType)}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "mostcomment"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.mostcomment"}}</a>
<a class="{{if eq .SortType "leastcomment"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.leastcomment"}}</a>
<a class="{{if eq .SortType "nearduedate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.nearduedate"}}</a>
<a class="{{if eq .SortType "farduedate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_sort.farduedate"}}</a>
</div>
</div>
</div>
</div>
</div>
<div id="issue-actions" class="ui stackable grid hide">
<div class="six wide column">
{{template "repo/issue/openclose" .}}
</div>
{{/* Ten wide does not cope well and makes the columns stack.
This seems to be related to jQuery's hide/show: in fact, switching
issue-actions and issue-filters and having this ten wide will show
this one correctly, but not the other one. */}}
<div class="nine wide right aligned right floated column">
<div class="ui secondary filter stackable menu">
{{if not .Repository.IsArchived}}
<!-- Action Button -->
{{if .IsShowClosed}}
<div class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.i18n.Tr "repo.issues.action_open"}}</div>
{{else}}
<div class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.i18n.Tr "repo.issues.action_close"}}</div>
{{end}}
<!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_label"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
{{range .Labels}}
<div class="item issue-action" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
{{if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}
</div>
{{end}}
</div>
</div>
<!-- Milestone -->
<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_milestone"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/milestone">
{{.i18n.Tr "repo.issues.action_milestone_no_select"}}
</div>
{{range .Milestones}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/milestone">
{{.Name}}
</div>
{{end}}
</div>
</div>
<!-- Projects -->
<div class="ui {{if not .Projects}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.project_board"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/projects">
{{.i18n.Tr "repo.issues.new.no_projects"}}
</div>
{{range .Projects}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects">
{{.Title}}
</div>
{{end}}
</div>
</div>
<!-- Assignees -->
<div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
<span class="text">
{{.i18n.Tr "repo.issues.action_assignee"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</span>
<div class="menu">
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/assignee">
{{.i18n.Tr "repo.issues.action_assignee_no_select"}}
</div>
{{range .Assignees}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
{{avatar .}} {{.GetDisplayName}}
</div>
{{end}}
</div>
</div>
{{end}}
</div>
</div>
</div>
{{template "shared/issuelist" mergeinto . "listType" "repo"}} {{template "shared/issuelist" mergeinto . "listType" "repo"}}
</div> </div>
</div> </div>

23
templates/repo/issue/nav_search_and_new.tmpl

@ -0,0 +1,23 @@
<div class="ui three column stackable grid">
<div class="column">
{{template "repo/issue/navbar" .}}
</div>
<div class="column center aligned">
{{template "repo/issue/search" .}}
</div>
{{if not .Repository.IsArchived}}
<div class="column right aligned">
{{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.Repository.Link}}/compare/{{.Repository.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{.i18n.Tr "repo.pulls.new"}}</a>
{{end}}
</div>
{{else}}
{{if not .PageIsIssueList}}
<div class="column right aligned">
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{if .PullRequestCtx.Allowed}}{{.PullRequestCtx.BaseRepo.Link}}/compare/{{.PullRequestCtx.BaseRepo.DefaultBranch | PathEscapeSegments}}...{{if ne .Repository.Owner.Name .PullRequestCtx.BaseRepo.Owner.Name}}{{PathEscape .Repository.Owner.Name}}:{{end}}{{.Repository.DefaultBranch | PathEscapeSegments}}{{end}}">{{$.i18n.Tr "action.compare_commits_general"}}</a>
</div>
{{end}}
{{end}}
</div>

1
templates/repo/issue/navbar.tmpl

@ -1,4 +1,5 @@
<div class="ui compact left small menu"> <div class="ui compact left small menu">
<a class="{{if .PageIsLabels}}active{{end}} item" href="{{.RepoLink}}/labels">{{.i18n.Tr "repo.labels"}}</a> <a class="{{if .PageIsLabels}}active{{end}} item" href="{{.RepoLink}}/labels">{{.i18n.Tr "repo.labels"}}</a>
<a class="{{if .PageIsMilestones}}active{{end}} item" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> <a class="{{if .PageIsMilestones}}active{{end}} item" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>
<a class="{{if .PageIsIssuesTree}}active{{end}} item" href="{{.RepoLink}}/issues_tree">{{.i18n.Tr "repo.issues_tree"}}</a>
</div> </div>

20
templates/repo/issue/treant_config.tmpl

@ -0,0 +1,20 @@
<style>
.chart {
height: 600px;
width: 100%;
border: 3px solid #DDD;
border-radius: 3px;
}
.comment {
display: inline-block;
width: 360px;
}
.comment { padding: 3px; border: 1px solid #ddd; border-radius: 3px; }
</style>
<link rel="stylesheet" href="https://fperucic.github.io/treant-js/Treant.css">
<script src="https://fperucic.github.io/treant-js/vendor/raphael.js"></script>
<script src="https://fperucic.github.io/treant-js/Treant.js"></script>

84
templates/repo/issue/tree.tmpl

@ -0,0 +1,84 @@
{{template "base/head" .}}
<div class="page-content repository">
{{template "repo/header" .}}
<div class="ui container">
{{template "repo/issue/nav_search_and_new" .}}
<div class="ui divider"></div>
{{template "repo/issue/issue_filters" .}}
{{template "repo/issue/issue_actions" .}}
<div hidden>
{{template "shared/issuelistfortree" mergeinto . "listType" "repo"}}
<div id="repo_title" class="comment"></div>
</div>
<script>
var source = document.getElementById("repo-title-in-header"),
destination = document.getElementById("repo_title");
destination.innerHTML = source.innerHTML
var task_post = [];
var config = {
container: "#TaskTreeChart",
levelSeparation: 60,
siblingSeparation: 60,
nodeAlign: "BOTTOM",
connectors: {
type: "step",
style: {
"stroke-width": 4,
"stroke": "#ccc",
"stroke-dasharray": "-", //"", "-", ".", "-.", "-..", ". ", "- ", "--", "- .", "--.", "--.."
"arrow-end": "classic-wide-long"
}
},
},
task_post0 = {
innerHTML: "#repo_title"
};
{{range .Issues}}
task_post[{{.ID}}] = {
parent: task_post0,
innerHTML: "#task{{.ID}}"
};
{{end}}
{{range .Issues}}
{{if .BlockedByParents}}
{{ $cur_id := .ID}}
{{ $cur_repo_id := .RepoID}}
{{range .BlockedByParents}}
{{if eq $cur_repo_id .Issue.RepoID}}
if( task_post[{{.Issue.ID}}] )
{
task_post[{{$cur_id}}].parent = task_post[{{.Issue.ID}}];
}
{{end}}
{{end}}
{{end}}
{{end}}
tree_structure = [
config, task_post0,
{{range .Issues}}
task_post[{{.ID}}],
{{end}}
];
</script>
<div class="chart Treant loaded" id="TaskTreeChart"></div>
{{template "repo/issue/treant_config" .}}
<script>
new Treant( tree_structure );
</script>
</div>
</div>
{{template "base/footer" .}}

50
templates/repo/issue/view_content/comments.tmpl

@ -831,5 +831,55 @@
{{end}} {{end}}
</span> </span>
</div> </div>
{{else if eq .Type 34}}
<div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-package-parents"}}</span>
<a href="{{.Poster.HomeLink}}">
{{avatar .Poster}}
</a>
<span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
{{$.i18n.Tr "repo.issues.parent.added_parent" $createdStr | Safe}}
</span>
{{if .ParentIssue}}
<div class="detail">
{{svg "octicon-plus"}}
<span class="text grey">
<a href="{{.ParentIssue.HTMLURL}}">
{{if eq .ParentIssue.RepoID .Issue.RepoID}}
#{{.ParentIssue.Index}} {{.ParentIssue.Title}}
{{else}}
{{.ParentIssue.Repo.FullName}}#{{.ParentIssue.Index}} - {{.ParentIssue.Title}}
{{end}}
</a>
</span>
</div>
{{end}}
</div>
{{else if eq .Type 35}}
<div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-package-parents"}}</span>
<a href="{{.Poster.HomeLink}}">
{{avatar .Poster}}
</a>
<span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
{{$.i18n.Tr "repo.issues.parent.removed_parent" $createdStr | Safe}}
</span>
{{if .ParentIssue}}
<div class="detail">
<span class="text grey">{{svg "octicon-trash"}}</span>
<span class="text grey">
<a href="{{.ParentIssue.HTMLURL}}">
{{if eq .ParentIssue.RepoID .Issue.RepoID}}
#{{.ParentIssue.Index}} {{.ParentIssue.Title}}
{{else}}
{{.ParentIssue.Repo.FullName}}#{{.ParentIssue.Index}} - {{.ParentIssue.Title}}
{{end}}
</a>
</span>
</div>
{{end}}
</div>
{{end}} {{end}}
{{end}} {{end}}

125
templates/repo/issue/view_content/sidebar.tmpl

@ -443,6 +443,131 @@
</div> </div>
{{end}} {{end}}
</div> </div>
{{if .Repository.IsParentsEnabled}}
<div class="ui divider"></div>
<div class="ui depending">
{{if (and (not .BlockedByParents) (not .BlockingParents))}}
<span class="text"><strong>{{.i18n.Tr "repo.issues.parent.title"}}</strong></span>
<br>
<p>
{{if .Issue.IsPull}}
{{.i18n.Tr "repo.issues.parent.pr_no_parents"}}
{{else}}
{{.i18n.Tr "repo.issues.parent.issue_no_parents"}}
{{end}}
</p>
{{end}}
{{if .BlockingParents}}
<span class="text tooltip" data-content="{{if .Issue.IsPull}}{{.i18n.Tr "repo.issues.parent.pr_close_blocks"}}{{else}}{{.i18n.Tr "repo.issues.parent.issue_close_blocks"}}{{end}}">
<strong>{{.i18n.Tr "repo.issues.parent.blocks_short"}}</strong>
</span>
<div class="ui relaxed divided list">
{{range .BlockingParents}}
<div class="item parent{{if .Issue.IsClosed}} is-closed{{end}} df ac sb">
<div class="item-left df jc fc f1">
<a class="title" href="{{.Issue.Link}}">
#{{.Issue.Index}} {{.Issue.Title | RenderEmoji}}
</a>
<div class="text small">
{{.Repository.OwnerName}}/{{.Repository.Name}}
</div>
</div>
<div class="item-right df ac">
{{if and $.CanCreateIssueParents (not $.Repository.IsArchived)}}
<a class="delete-parent-button tooltip ci muted" data-id="{{.Issue.ID}}" data-type="child" data-content="{{$.i18n.Tr "repo.issues.parent.remove_info"}}" data-inverted="">
{{svg "octicon-trash" 16}}
</a>
{{end}}
</div>
</div>
{{end}}
</div>
{{end}}
{{if .BlockedByParents}}
<span class="text tooltip" data-content="{{if .Issue.IsPull}}{{.i18n.Tr "repo.issues.parent.pr_closing_blockedby"}}{{else}}{{.i18n.Tr "repo.issues.parent.issue_closing_blockedby"}}{{end}}">
<strong>{{.i18n.Tr "repo.issues.parent.blocked_by_short"}}</strong>
</span>
<div class="ui relaxed divided list">
{{range .BlockedByParents}}
<div class="item parent{{if .Issue.IsClosed}} is-closed{{end}} df ac sb">
<div class="item-left df jc fc f1">
<a class="title" href="{{.Issue.Link}}">
#{{.Issue.Index}} {{.Issue.Title | RenderEmoji}}
</a>
<div class="text small">
{{.Repository.OwnerName}}/{{.Repository.Name}}
</div>
</div>
<div class="item-right df ac">
{{if and $.CanCreateIssueParents (not $.Repository.IsArchived)}}
<a class="delete-parent-button tooltip ci muted" data-id="{{.Issue.ID}}" data-type="father" data-content="{{$.i18n.Tr "repo.issues.parent.remove_info"}}" data-inverted="">
{{svg "octicon-trash" 16}}
</a>
{{end}}
</div>
</div>
{{end}}
</div>
{{end}}
{{if and .CanCreateIssueParents (not .Repository.IsArchived)}}
<div>
<form method="POST" action="{{.Issue.Link}}/parent/add" id="addParentForm">
{{$.CsrfTokenHtml}}
<div class="ui fluid action input">
<div class="ui search selection dropdown" id="new-parent-drop-list" data-issue-id="{{.Issue.ID}}">
<input name="newParent" type="hidden">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<input type="text" class="search">
<div class="default text">{{.i18n.Tr "repo.issues.parent.add"}}</div>
</div>
<button class="ui green icon button">
{{svg "octicon-plus"}}
</button>
</div>
</form>
</div>
{{end}}
</div>
{{if and .CanCreateIssueParents (not .Repository.IsArchived)}}
<input type="hidden" id="crossRepoSearch" value="{{.AllowCrossRepositoryParents}}">
<div class="ui basic modal remove-parent">
<div class="ui icon header">
{{svg "octicon-trash"}}
{{.i18n.Tr "repo.issues.parent.remove_header"}}
</div>
<div class="content">
<form method="POST" action="{{.Issue.Link}}/parent/delete" id="removeParentForm">
{{$.CsrfTokenHtml}}
<input type="hidden" value="" name="removeParentID" id="removeParentID"/>
<input type="hidden" value="" name="parentType" id="parentType"/>
</form>
<p>{{if .Issue.IsPull}}
{{.i18n.Tr "repo.issues.parent.pr_remove_text"}}
{{else}}
{{.i18n.Tr "repo.issues.parent.issue_remove_text"}}
{{end}}</p>
</div>
<div class="actions">
<div class="ui red cancel inverted button">
{{svg "octicon-x"}}
{{.i18n.Tr "repo.issues.parent.cancel"}}
</div>
<div class="ui green ok inverted button">
{{svg "octicon-check"}}
{{.i18n.Tr "repo.issues.parent.remove"}}
</div>
</div>
</div>
{{end}}
{{end}}
{{if .Repository.IsDependenciesEnabled}} {{if .Repository.IsDependenciesEnabled}}
<div class="ui divider"></div> <div class="ui divider"></div>

6
templates/repo/settings/options.tmpl

@ -329,6 +329,12 @@
<label>{{.i18n.Tr "repo.issues.dependency.setting"}}</label> <label>{{.i18n.Tr "repo.issues.dependency.setting"}}</label>
</div> </div>
</div> </div>
<div class="field">
<div class="ui checkbox">
<input name="enable_issue_parents" type="checkbox" {{if (.Repository.IsParentsEnabled)}}checked{{end}}>
<label>{{.i18n.Tr "repo.issues.parent.setting"}}</label>
</div>
</div>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="enable_close_issues_via_commit_in_any_branch" type="checkbox" {{ if .Repository.CloseIssuesViaCommitInAnyBranch }}checked{{end}}> <input name="enable_close_issues_via_commit_in_any_branch" type="checkbox" {{ if .Repository.CloseIssuesViaCommitInAnyBranch }}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.admin_enable_close_issues_via_commit_in_any_branch"}}</label> <label>{{.i18n.Tr "repo.settings.admin_enable_close_issues_via_commit_in_any_branch"}}</label>

2
templates/repo/user_cards.tmpl

@ -16,7 +16,7 @@
{{if .Website}} {{if .Website}}
{{svg "octicon-link"}} <a href="{{.Website}}" target="_blank" rel="noopener noreferrer">{{.Website}}</a> {{svg "octicon-link"}} <a href="{{.Website}}" target="_blank" rel="noopener noreferrer">{{.Website}}</a>
{{else if .Location}} {{else if .Location}}
{{svg "octicon-location"}} {{.Location}} {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}})
{{else}} {{else}}
{{svg "octicon-clock"}} {{$.i18n.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}} {{svg "octicon-clock"}} {{$.i18n.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}}
{{end}} {{end}}

144
templates/shared/issuelistfortree.tmpl

@ -0,0 +1,144 @@
<div class="issue list">
{{ $approvalCounts := .ApprovalCounts}}
{{range .Issues}}
<div id="task{{.ID}}" class="issue list comment">
<li class="item df py-3">
<div class="issue-item-left df">
{{if $.CanWriteIssuesOrPulls}}
<div class="ui checkbox issue-checkbox">
<input type="checkbox" data-issue-id={{.ID}}></input>
<label></label>
</div>
{{end}}
<div class="issue-item-icon">
{{if .IsPull}}
{{if .PullRequest.HasMerged}}
{{svg "octicon-git-merge" 16 "text purple"}}
{{else}}
{{if .IsClosed}}
{{svg "octicon-git-pull-request" 16 "text red"}}
{{else}}
{{svg "octicon-git-pull-request" 16 "text green"}}
{{end}}
{{end}}
{{else}}
{{if .IsClosed}}
{{svg "octicon-issue-closed" 16 "text red"}}
{{else}}
{{svg "octicon-issue-opened" 16 "text green"}}
{{end}}
{{end}}
</div>
</div>
<div class="issue-item-main f1 fc df">
<div class="issue-item-top-row">
<a class="title tdn" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji .Title}}</a>
{{if .IsPull}}
{{if (index $.CommitStatus .PullRequest.ID)}}
{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
{{end}}
{{end}}
<span class="labels-list ml-2">
{{range .Labels}}
<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description | RenderEmojiPlain}}">{{.Name | RenderEmoji}}</a>
{{end}}
</span>
</div>
<div class="desc issue-item-bottom-row df ac fw my-1">
<a class="index ml-0 mr-2" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{if eq $.listType "dashboard"}}
{{.Repo.FullName}}#{{.Index}}
{{else}}
#{{.Index}}
{{end}}
</a>
{{ $timeStr := TimeSinceUnix .GetLastEventTimestamp $.Lang }}
{{if .OriginalAuthor }}
{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.OriginalAuthor|Escape) | Safe}}
{{else if gt .Poster.ID 0}}
{{$.i18n.Tr .GetLastEventLabel $timeStr (.Poster.HomeLink|Escape) (.Poster.GetDisplayName | Escape) | Safe}}
{{else}}
{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
{{end}}
{{if and .Milestone (ne $.listType "milestone")}}
<a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
{{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}}
</a>
{{end}}
{{if .Ref}}
<a class="ref" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
{{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}}
</a>
{{end}}
{{$tasks := .GetTasks}}
{{if gt $tasks 0}}
{{$tasksDone := .GetTasksDone}}
<span class="checklist">
{{svg "octicon-checklist" 14 "mr-2"}}{{$tasksDone}} / {{$tasks}} <span class="progress-bar"><span class="progress" style="width:calc(100% * {{$tasksDone}} / {{$tasks}});"></span></span>
</span>
{{end}}
{{if ne .DeadlineUnix 0}}
<span class="due-date tooltip" data-content="{{$.i18n.Tr "repo.issues.due_date"}}" data-position="right center">
<span{{if .IsOverdue}} class="overdue"{{end}}>
{{svg "octicon-calendar" 14 "mr-2"}}
{{.DeadlineUnix.FormatShort}}
</span>
</span>
{{end}}
{{if .IsPull}}
{{$approveOfficial := call $approvalCounts .ID "approve"}}
{{$rejectOfficial := call $approvalCounts .ID "reject"}}
{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
{{if gt $approveOfficial 0}}
<span class="approvals df ac">
{{svg "octicon-check" 14 "mr-1"}}
{{$.i18n.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}}
</span>
{{end}}
{{if gt $rejectOfficial 0}}
<span class="rejects df ac">
{{svg "octicon-diff" 14 "mr-2"}}
{{$.i18n.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}}
</span>
{{end}}
{{if gt $waitingOfficial 0}}
<span class="waiting df ac">
{{svg "octicon-eye" 14 "mr-2"}}
{{$.i18n.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}}
</span>
{{end}}
{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
<span class="conflicting df ac">
{{svg "octicon-x" 14}}
{{$.i18n.TrN (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n" (len .PullRequest.ConflictedFiles)}}
</span>
{{end}}
{{end}}
</div>
</div>
<div class="issue-item-icons-right df p-2">
<div class="issue-item-icon-right text grey">
{{if .TotalTrackedTime}}
{{svg "octicon-clock" 16 "mr-2"}}
{{.TotalTrackedTime | Sec2Time}}
{{end}}
</div>
<div class="issue-item-icon-right text grey">
{{range .Assignees}}
<a class="ui assignee tooltip tdn" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-position="left center">
{{avatar .}}
</a>
{{end}}
</div>
<div class="issue-item-icon-right text grey">
{{if .NumComments}}
<a class="tdn" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16 "mr-2"}}{{.NumComments}}
</a>
{{end}}
</div>
</div>
</li>
</div>
{{end}}
</div>

5
templates/swagger/v1_json.tmpl

@ -15592,6 +15592,11 @@
"type": "boolean", "type": "boolean",
"x-go-name": "EnableIssueDependencies" "x-go-name": "EnableIssueDependencies"
}, },
"enable_issue_parents": {
"description": "Enable parents for issues and pull requests (Built-in issue tracker)",
"type": "boolean",
"x-go-name": "EnableIssueParents"
},
"enable_time_tracker": { "enable_time_tracker": {
"description": "Enable time tracking (Built-in issue tracker)", "description": "Enable time tracking (Built-in issue tracker)",
"type": "boolean", "type": "boolean",

2
templates/user/profile.tmpl

@ -20,7 +20,7 @@
<div class="extra content word-break"> <div class="extra content word-break">
<ul> <ul>
{{if .Owner.Location}} {{if .Owner.Location}}
<li>{{svg "octicon-location"}} {{.Owner.Location}}</li> <li>{{svg "octicon-location"}} {{.Owner.Location}} ({{.Owner.LocationCoordinate}})</li>
{{end}} {{end}}
{{if .ShowUserEmail }} {{if .ShowUserEmail }}
<li> <li>

4
templates/user/settings/profile.tmpl

@ -46,6 +46,10 @@
<label for="location">{{.i18n.Tr "settings.location"}}</label> <label for="location">{{.i18n.Tr "settings.location"}}</label>
<input id="location" name="location" value="{{.SignedUser.Location}}"> <input id="location" name="location" value="{{.SignedUser.Location}}">
</div> </div>
<div class="field">
<label for="location_coordinate">{{.i18n.Tr "settings.locationcoordinate"}}</label>
<input id="location_coordinate" name="location_coordinate" value="{{.SignedUser.LocationCoordinate}}" placeholder="55.4, 37.3">
</div>
<div class="ui divider"></div> <div class="ui divider"></div>
<!-- private block --> <!-- private block -->

46
web_src/js/features/repo-issue.js

@ -121,6 +121,34 @@ export function initRepoIssueList() {
fullTextSearch: true, fullTextSearch: true,
}); });
$('#new-parent-drop-list')
.dropdown({
apiSettings: {
url: issueSearchUrl,
onResponse(response) {
const filteredResponse = {success: true, results: []};
const currIssueId = $('#new-parent-drop-list').data('issue-id');
// Parse the response from the api to work with our dropdown
$.each(response, (_i, issue) => {
// Don't list current issue in the parent list.
if (issue.id === currIssueId) {
return;
}
filteredResponse.results.push({
name: `#${issue.number} ${htmlEscape(issue.title)
}<div class="text small dont-break-out">${htmlEscape(issue.repository.full_name)}</div>`,
value: issue.id,
});
});
return filteredResponse;
},
cache: false,
},
fullTextSearch: true,
});
function excludeLabel(item) { function excludeLabel(item) {
const href = $(item).attr('href'); const href = $(item).attr('href');
const id = $(item).data('label-id'); const id = $(item).data('label-id');
@ -196,6 +224,24 @@ export function initRepoIssueDependencyDelete() {
}); });
} }
export function initRepoIssueParentDelete() {
// Delete Issue parent
$(document).on('click', '.delete-parent-button', (e) => {
const id = e.currentTarget.getAttribute('data-id');
const type = e.currentTarget.getAttribute('data-type');
$('.remove-parent').modal({
closable: false,
duration: 200,
onApprove: () => {
$('#removeParentID').val(id);
$('#parentType').val(type);
$('#removeParentForm').trigger('submit');
},
}).modal('show');
});
}
export function initRepoIssueCodeCommentCancel() { export function initRepoIssueCodeCommentCancel() {
// Cancel inline code comment // Cancel inline code comment
$(document).on('click', '.cancel-code-comment', (e) => { $(document).on('click', '.cancel-code-comment', (e) => {

3
web_src/js/features/repo-legacy.js

@ -4,7 +4,7 @@ import {initCompImagePaste, initEasyMDEImagePaste} from './comp/ImagePaste.js';
import { import {
initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel,
initRepoIssueCommentDelete, initRepoIssueCommentDelete,
initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueParentDelete,
initRepoIssueReferenceIssue, initRepoIssueStatusButton, initRepoIssueReferenceIssue, initRepoIssueStatusButton,
initRepoIssueTitleEdit, initRepoIssueTitleEdit,
initRepoIssueWipToggle, initRepoPullRequestMerge, initRepoPullRequestUpdate, initRepoIssueWipToggle, initRepoPullRequestMerge, initRepoPullRequestUpdate,
@ -516,6 +516,7 @@ export function initRepository() {
initRepoIssueCommentDelete(); initRepoIssueCommentDelete();
initRepoIssueDependencyDelete(); initRepoIssueDependencyDelete();
initRepoIssueParentDelete();
initRepoIssueCodeCommentCancel(); initRepoIssueCodeCommentCancel();
initRepoIssueStatusButton(); initRepoIssueStatusButton();
initRepoPullRequestMerge(); initRepoPullRequestMerge();

15
web_src/less/_repository.less

@ -2929,6 +2929,21 @@ tbody.commit-list {
} }
} }
#new-parent-drop-list {
&.ui.selection.dropdown {
min-width: 0;
width: 100%;
border-radius: 4px 0 0 4px;
border-right: 0;
white-space: nowrap;
}
.text {
width: 100%;
overflow: hidden;
}
}
#manage_topic { #manage_topic {
font-size: 12px; font-size: 12px;
} }

Loading…
Cancel
Save