diff --git a/README.md b/README.md index 84c0524ed4..5a5a06f64c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@

- - Gitea + + Мирокод

-

Gitea - Git with a cup of tea

+

Платформа ЦРНП "Мирокод" для разработки проектов

+ +## Общее описание + +Платформа "Мирокод" - необходимый инструмент для разработки народных проектов. + +Разрабатывается на основе gitea, в перспективе планируется реализовать: +1. Карта пользователей (их ресурсы, компетенции и интересы), проектов, сообществ. +2. Дерево задач в каждом проекте +3. Дерево проектов +4. Проекты могут быть не только для разработки, но и любые общественные проекты +5. У пользователя можно будет указать ресурсы, компетенции, интересы +6. Объединяются в одном месте, люди, проекты, ресурсы и компетенции, а также инструменты ведения проектов и решения задач. +7. В перспективе самый простой модуль игроподобия, для того чтобы легко подбирать новые задачи из любых проектов +8. Метрика по проектам + +Проект создан на основе: [forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from [Gogs](https://gogs.io) since 2016.11 but changed a lot. -## Building - -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 `) 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)] - - - -## 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)] - - - - - - - - - - - - -## 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). +Подробное описание сборки и пр. - http://git.mirocod.ru/MIROCOD/Platform_Mirocod/wiki -## License +## Лицензия This project is licensed under the MIT License. See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 8d7946145f..dcced2772d 100644 --- a/custom/conf/app.example.ini +++ b/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. ;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_USER_HEATMAP = true ;; diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go index 91ec4c699e..3f428065ef 100644 --- a/integrations/api_repo_edit_test.go +++ b/integrations/api_repo_edit_test.go @@ -35,6 +35,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption EnableTimeTracker: config.EnableTimetracker, AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, EnableIssueDependencies: config.EnableDependencies, + EnableIssueParents: config.EnableParents, } } else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil { config := unit.ExternalTrackerConfig() @@ -182,6 +183,7 @@ func TestAPIRepoEdit(t *testing.T) { EnableTimeTracker: false, AllowOnlyContributorsToTrackTime: false, EnableIssueDependencies: false, + EnableIssueParents: false, } *repoEditOption.HasWiki = true repoEditOption.ExternalWiki = nil diff --git a/models/error.go b/models/error.go index 1d0f658eb8..1afac85109 100644 --- a/models/error.go +++ b/models/error.go @@ -1340,6 +1340,91 @@ func (err ErrUnknownDependencyType) Error() string { 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) +} + // __________ .__ // \______ \ _______ _|__| ______ _ __ // | _// __ \ \/ / |/ __ \ \/ \/ / diff --git a/models/issue.go b/models/issue.go index f2552c0a1e..e4dd358ad9 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1990,6 +1990,12 @@ type DependencyInfo struct { 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 func (issue *Issue) getParticipantIDsByIssue(e db.Engine) ([]int64, error) { if issue == nil { @@ -2048,6 +2054,42 @@ func (issue *Issue) getBlockingDependencies(e db.Engine) (issueDeps []*Dependenc 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 func (issue *Issue) BlockedByDependencies() ([]*DependencyInfo, error) { return issue.getBlockedByDependencies(db.GetEngine(db.DefaultContext)) @@ -2058,6 +2100,16 @@ func (issue *Issue) BlockingDependencies() ([]*DependencyInfo, error) { 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) { if issue.IsPull { err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") diff --git a/models/issue_comment.go b/models/issue_comment.go index f4a6b3ce13..e3dc31233f 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -108,6 +108,10 @@ const ( CommentTypeDismissReview // 33 Change issue ref CommentTypeChangeIssueRef + // 34 Parent added + CommentTypeAddParent + // 35 Parent removed + CommentTypeRemoveParent ) var commentStrings = []string{ @@ -145,6 +149,8 @@ var commentStrings = []string{ "project_board", "dismiss_review", "change_issue_ref", + "add_parent", + "remove_parent", } func (t CommentType) String() string { @@ -224,6 +230,8 @@ type Comment struct { NewRef string DependentIssueID int64 DependentIssue *Issue `xorm:"-"` + ParentIssueID int64 + ParentIssue *Issue `xorm:"-"` CommitID int64 Line int64 // - previous line / + proposed line @@ -619,6 +627,15 @@ func (c *Comment) LoadDepIssueDetails() (err error) { 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 func (c *Comment) LoadTime() error { if c.Time != nil || c.TimeID == 0 { @@ -789,6 +806,7 @@ func createComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, OldRef: opts.OldRef, NewRef: opts.NewRef, DependentIssueID: opts.DependentIssueID, + ParentIssueID: opts.ParentIssueID, TreePath: opts.TreePath, ReviewID: opts.ReviewID, Patch: opts.Patch, @@ -933,6 +951,39 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is 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 type CreateCommentOptions struct { Type CommentType @@ -942,6 +993,7 @@ type CreateCommentOptions struct { Label *Label DependentIssueID int64 + ParentIssueID int64 OldMilestoneID int64 MilestoneID int64 OldProjectID int64 diff --git a/models/issue_comment_list.go b/models/issue_comment_list.go index 23a2756dcf..c614535f84 100644 --- a/models/issue_comment_list.go +++ b/models/issue_comment_list.go @@ -395,6 +395,69 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { 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) { if len(comments) == 0 { return nil @@ -528,6 +591,10 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) { return } + if err = comments.loadParentIssues(ctx); err != nil { + return + } + return nil } diff --git a/models/issue_parent.go b/models/issue_parent.go new file mode 100644 index 0000000000..9e4780fbfb --- /dev/null +++ b/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 +} diff --git a/models/issue_parent_test.go b/models/issue_parent_test.go new file mode 100644 index 0000000000..1fd28fd47b --- /dev/null +++ b/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) +} diff --git a/models/migrations/v70.go b/models/migrations/v70.go index 7d34c89d11..868d43295b 100644 --- a/models/migrations/v70.go +++ b/models/migrations/v70.go @@ -102,6 +102,9 @@ func addIssueDependencies(x *xorm.Engine) (err error) { if _, ok := unit.Config["EnableDependencies"]; !ok { 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 { return err } diff --git a/models/org_team.go b/models/org_team.go index 77acddc0e0..61b7ff9e7e 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -691,8 +691,8 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) { return errors.New("empty team name") } - if len(t.Description) > 255 { - t.Description = t.Description[:255] + if len(t.Description) > 1024 { + t.Description = t.Description[:1024] } ctx, committer, err := db.TxContext() diff --git a/models/repo.go b/models/repo.go index 29bb196e8e..606a89008c 100644 --- a/models/repo.go +++ b/models/repo.go @@ -515,6 +515,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_ EnableTimetracker: setting.Service.DefaultEnableTimetracking, AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime, EnableDependencies: setting.Service.DefaultEnableDependencies, + EnableParents: setting.Service.DefaultEnableParents, }, }) } 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) { repo.LowerName = strings.ToLower(repo.Name) - if utf8.RuneCountInString(repo.Description) > 255 { - repo.Description = string([]rune(repo.Description)[:255]) + if utf8.RuneCountInString(repo.Description) > 1024 { + repo.Description = string([]rune(repo.Description)[:1024]) } if utf8.RuneCountInString(repo.Website) > 255 { repo.Website = string([]rune(repo.Website)[:255]) diff --git a/models/repo/issue.go b/models/repo/issue.go index 9f0fa3bad9..e09532ca60 100644 --- a/models/repo/issue.go +++ b/models/repo/issue.go @@ -70,3 +70,19 @@ func (repo *Repository) IsDependenciesEnabledCtx(ctx context.Context) bool { } 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 +} diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index f526cbdf8b..2414c43462 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -94,6 +94,7 @@ type IssuesConfig struct { EnableTimetracker bool AllowOnlyContributorsToTrackTime bool EnableDependencies bool + EnableParents bool } // FromDB fills up a IssuesConfig from serialized format. diff --git a/models/user/user.go b/models/user/user.go index e375f3de0a..ac70c242b5 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -89,16 +89,17 @@ type User struct { // is to change his/her password after registration. MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` - LoginType auth.Type - LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` - LoginName string - Type UserType - Location string - Website string - Rands string `xorm:"VARCHAR(32)"` - Salt string `xorm:"VARCHAR(32)"` - Language string `xorm:"VARCHAR(5)"` - Description string + LoginType auth.Type + LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` + LoginName string + Type UserType + Location string + LocationCoordinate string + Website string + Rands string `xorm:"VARCHAR(32)"` + Salt string `xorm:"VARCHAR(32)"` + Language string `xorm:"VARCHAR(5)"` + Description string `xorm:"TEXT"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` @@ -187,8 +188,9 @@ func (u *User) BeforeUpdate() { u.LowerName = strings.ToLower(u.Name) u.Location = base.TruncateString(u.Location, 255) + u.LocationCoordinate = base.TruncateString(u.LocationCoordinate, 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. diff --git a/modules/context/repo.go b/modules/context/repo.go index 4eeab710ff..cd5259d4f8 100644 --- a/modules/context/repo.go +++ b/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) } +// 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 func (r *Repository) GetCommitsCount() (int64, error) { var contextName string diff --git a/modules/convert/convert.go b/modules/convert/convert.go index 41a044c6d7..bbb89f97e6 100644 --- a/modules/convert/convert.go +++ b/modules/convert/convert.go @@ -289,6 +289,7 @@ func ToOrganization(org *models.Organization) *api.Organization { Description: org.Description, Website: org.Website, Location: org.Location, + LocationCoordinate: org.LocationCoordinate, Visibility: org.Visibility.String(), RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess, } diff --git a/modules/convert/issue_comment.go b/modules/convert/issue_comment.go index caba2b506e..061fe59142 100644 --- a/modules/convert/issue_comment.go +++ b/modules/convert/issue_comment.go @@ -52,6 +52,12 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo return nil } + err = c.LoadParentIssueDetails() + if err != nil { + log.Error("LoadParentIssueDetails: %v", err) + return nil + } + err = c.LoadTime() if err != nil { 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) } + if c.ParentIssue != nil { + comment.ParentIssue = ToAPIIssue(c.ParentIssue) + } + return comment } diff --git a/modules/convert/repository.go b/modules/convert/repository.go index 459f98f396..ee320eaf6f 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -51,6 +51,7 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo EnableTimeTracker: config.EnableTimetracker, AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, EnableIssueDependencies: config.EnableDependencies, + EnableIssueParents: config.EnableParents, } } else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil { config := unit.ExternalTrackerConfig() diff --git a/modules/convert/user.go b/modules/convert/user.go index dc4a8c49c7..5f9ba9fc3d 100644 --- a/modules/convert/user.go +++ b/modules/convert/user.go @@ -55,6 +55,7 @@ func toUser(user *user_model.User, signed, authed bool) *api.User { Created: user.CreatedUnix.AsTime(), Restricted: user.IsRestricted, Location: user.Location, + LocationCoordinate: user.LocationCoordinate, Website: user.Website, Description: user.Description, // counter's @@ -87,6 +88,7 @@ func User2UserSettings(user *user_model.User) api.UserSettings { FullName: user.FullName, Website: user.Website, Location: user.Location, + LocationCoordinate: user.LocationCoordinate, Language: user.Language, Description: user.Description, Theme: user.Theme, diff --git a/modules/doctor/fix16961.go b/modules/doctor/fix16961.go index 56d02ae92e..7843645d00 100644 --- a/modules/doctor/fix16961.go +++ b/modules/doctor/fix16961.go @@ -206,6 +206,10 @@ func fixIssuesConfig16961(bs []byte, cfg *repo_model.IssuesConfig) (fixed bool, if parseErr != nil { return } + cfg.EnableParents, parseErr = parseBool16961(parts[3]) + if parseErr != nil { + return + } return true, nil } diff --git a/modules/doctor/fix16961_test.go b/modules/doctor/fix16961_test.go index f5e5667c09..380833cd1c 100644 --- a/modules/doctor/fix16961_test.go +++ b/modules/doctor/fix16961_test.go @@ -237,11 +237,12 @@ func Test_fixIssuesConfig_16961(t *testing.T) { }{ { name: "normal", - bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true}`, + bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true,"EnableParents":true}`, expected: repo_model.IssuesConfig{ EnableTimetracker: true, AllowOnlyContributorsToTrackTime: true, EnableDependencies: true, + EnableParents: true, }, }, { @@ -251,6 +252,7 @@ func Test_fixIssuesConfig_16961(t *testing.T) { EnableTimetracker: true, AllowOnlyContributorsToTrackTime: true, EnableDependencies: true, + EnableParents: true, }, wantFixed: true, }, diff --git a/modules/setting/service.go b/modules/setting/service.go index a391926382..6505f444a4 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -53,7 +53,9 @@ var Service = struct { EnableTimetracking bool DefaultEnableTimetracking bool DefaultEnableDependencies bool + DefaultEnableParents bool AllowCrossRepositoryDependencies bool + AllowCrossRepositoryParents bool DefaultAllowOnlyContributorsToTrackTime bool NoReplyAddress string EnableUserHeatmap bool @@ -141,7 +143,9 @@ func newService() { Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").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.AllowCrossRepositoryParents = sec.Key("ALLOW_CROSS_REPOSITORY_PARENTS").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.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true) diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index eccbf29a46..ea52caa64a 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -36,6 +36,7 @@ type EditUserOption struct { MustChangePassword *bool `json:"must_change_password"` Website *string `json:"website" binding:"OmitEmpty;ValidUrl;MaxSize(255)"` Location *string `json:"location" binding:"MaxSize(50)"` + LocationCoordinate *string `json:"location_coordinate" binding:"MaxSize(255)"` Description *string `json:"description" binding:"MaxSize(255)"` Active *bool `json:"active"` Admin *bool `json:"admin"` diff --git a/modules/structs/issue_comment.go b/modules/structs/issue_comment.go index e13ec05d01..d6bf5ef887 100644 --- a/modules/structs/issue_comment.go +++ b/modules/structs/issue_comment.go @@ -79,4 +79,6 @@ type TimelineComment struct { ResolveDoer *User `json:"resolve_doer"` DependentIssue *Issue `json:"dependent_issue"` + + ParentIssue *Issue `json:"parent_issue"` } diff --git a/modules/structs/org.go b/modules/structs/org.go index d8bd59e1ec..622303047c 100644 --- a/modules/structs/org.go +++ b/modules/structs/org.go @@ -13,6 +13,7 @@ type Organization struct { Description string `json:"description"` Website string `json:"website"` Location string `json:"location"` + LocationCoordinate string `json:"location_coordinate"` Visibility string `json:"visibility"` RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` } @@ -31,9 +32,10 @@ type CreateOrgOption struct { // required: true UserName string `json:"username" binding:"Required"` 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)"` Location string `json:"location" binding:"MaxSize(50)"` + LocationCoordinate string `json:"location_coordinate" binding:"MaxSize(255)"` // possible values are `public` (default), `limited` or `private` // enum: 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 type EditOrgOption struct { 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)"` Location string `json:"location" binding:"MaxSize(50)"` + LocationCoordinate string `json:"location_coordinate" binding:"MaxSize(255)"` // possible values are `public`, `limited` or `private` // enum: public,limited,private Visibility string `json:"visibility" binding:"In(,public,limited,private)"` diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go index 53e3fcf62d..6d2b502f00 100644 --- a/modules/structs/org_team.go +++ b/modules/structs/org_team.go @@ -26,7 +26,7 @@ type Team struct { type CreateTeamOption struct { // required: true 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"` // enum: read,write,admin Permission string `json:"permission"` @@ -42,7 +42,7 @@ type CreateTeamOption struct { type EditTeamOption struct { // required: true 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"` // enum: read,write,admin Permission string `json:"permission"` diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 178704ab4f..4ef1d5a2f6 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -25,6 +25,8 @@ type InternalTracker struct { AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"` // Enable dependencies for issues and pull requests (Built-in issue tracker) 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 @@ -107,7 +109,7 @@ type CreateRepoOption struct { // unique: true Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` // 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 Private bool `json:"private"` // Label-Set to use @@ -136,7 +138,7 @@ type EditRepoOption struct { // unique: true Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"` // 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. Website *string `json:"website,omitempty" binding:"MaxSize(255)"` // either `true` to make the repository private or `false` to make it public. @@ -200,7 +202,7 @@ type GenerateRepoOption struct { // unique: true Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` // 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 Private bool `json:"private"` // include git content of default branch in template repo @@ -309,7 +311,7 @@ type MigrateRepoOptions struct { LFS bool `json:"lfs"` LFSEndpoint string `json:"lfs_endpoint"` Private bool `json:"private"` - Description string `json:"description" binding:"MaxSize(255)"` + Description string `json:"description" binding:"MaxSize(1024)"` Wiki bool `json:"wiki"` Milestones bool `json:"milestones"` Labels bool `json:"labels"` diff --git a/modules/structs/user.go b/modules/structs/user.go index 431e230fac..322a30047c 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -39,6 +39,8 @@ type User struct { ProhibitLogin bool `json:"prohibit_login"` // the user's location Location string `json:"location"` + // the user's location coordinate + LocationCoordinate string `json:"location_coordinate"` // the user's website Website string `json:"website"` // the user's description @@ -69,6 +71,7 @@ type UserSettings struct { Website string `json:"website"` Description string `json:"description"` Location string `json:"location"` + LocationCoordinate string `json:"location_coordinate"` Language string `json:"language"` Theme string `json:"theme"` DiffViewStyle string `json:"diff_view_style"` @@ -82,8 +85,9 @@ type UserSettings struct { type UserSettingsOptions struct { FullName *string `json:"full_name" binding:"MaxSize(100)"` 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)"` + LocationCoordinate *string `json:"location_coordinate" binding:"MaxSize(255)"` Language *string `json:"language"` Theme *string `json:"theme"` DiffViewStyle *string `json:"diff_view_style"` diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 0111dfe434..7a3a9bb10d 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -22,7 +22,7 @@ signed_in_as=Вы вошли как enable_javascript=Пожалуйста, включите JavaScript. toc=Содержание licenses=Лицензии -return_to_gitea=Вернуться к Gitea +return_to_gitea=Вернуться к Мирокод username=Имя пользователя email=Адрес эл. почты @@ -46,23 +46,24 @@ webauthn_error_unable_to_process=Сервер не смог обработать webauthn_error_duplicated=Представленный ключ не подходит для этого запроса. Если вы пытаетесь зарегистрировать его, убедитесь, что ключ ещё не зарегистрирован. webauthn_error_empty=Вы должны указать имя для этого ключа. -repository=Репозиторий -organization=Организация +repository=Проект +organization=Сообщество mirror=Зеркало -new_repo=Новый репозиторий +new_repo=Новый проект new_migrate=Новая миграция new_mirror=Новое зеркало -new_fork=Новый форк репозитория -new_org=Новая организация -new_project=Новый проект +new_fork=Новый форк проекта +new_org=Новое сообщество +new_project=Новое направление new_project_board=Новая доска проекта -manage_org=Управление организациями +manage_org=Управление сообществами admin_panel=Панель управления account_settings=Настройки аккаунта settings=Настройки your_profile=Профиль your_starred=Избранные your_settings=Настройки +map=Карта all=Все sources=Собственные @@ -110,17 +111,17 @@ app_desc=Удобный сервис собственного хостинга install=Простой в установке install_desc=Просто запустите исполняемый файл для вашей платформы, разверните через Docker, или установите с помощью менеджера пакетов. platform=Кроссплатформенный -platform_desc=Gitea работает на любой операционной системе, которая может компилировать Go: Windows, macOS, Linux, ARM и т. д. Выбирайте, что вам больше нравится! +platform_desc=Мирокод работает на любой операционной системе, которая может компилировать Go: Windows, macOS, Linux, ARM и т. д. Выбирайте, что вам больше нравится! lightweight=Легковесный -lightweight_desc=Gitea имеет низкие системные требования и может работать на недорогом Raspberry Pi. Экономьте энергию вашей машины! +lightweight_desc=Мирокод имеет низкие системные требования и может работать на недорогом Raspberry Pi. Экономьте энергию вашей машины! license=Открытый исходный код -license_desc=Всё это на code.gitea.io/gitea! Присоединяйтесь к нам, внося вклад, чтобы сделать этот проект ещё лучше. Не бойтесь помогать! +license_desc=Всё это на code.gitea.io/gitea! Присоединяйтесь к нам, внося вклад, чтобы сделать это направление ещё лучше. Не бойтесь помогать! [install] install=Установка title=Начальная конфигурация -docker_helper=Если вы запускаете Gitea внутри Docker, пожалуйста внимательно прочтите документацию перед тем, как изменить любые настройки. -require_db_desc=Gitea требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL). +docker_helper=Если вы запускаете Мирокод внутри Docker, пожалуйста внимательно прочтите документацию перед тем, как изменить любые настройки. +require_db_desc=Мирокод требует MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (через протокол MySQL). db_title=Настройки базы данных db_type=Тип базы данных host=Хост @@ -133,12 +134,12 @@ db_schema_helper=Оставьте пустым для значения по ум ssl_mode=SSL charset=Кодировка path=Путь -sqlite_helper=Путь к файлу базы данных SQLite3.
Введите абсолютный путь, если вы запускаете Gitea как службу. -reinstall_error=Вы пытаетесь произвести установку в уже существующую базу данных Gitea -reinstall_confirm_message=Переустановка в уже существующую базу данных Gitea может вызвать несколько проблем. В большинстве случаев вы должны использовать существующий "app.ini" для запуска Gitea. Если вы понимаете, что вы делаете, подтвердите: +sqlite_helper=Путь к файлу базы данных SQLite3.
Введите абсолютный путь, если вы запускаете Мирокод как службу. +reinstall_error=Вы пытаетесь произвести установку в уже существующую базу данных Мирокод +reinstall_confirm_message=Переустановка в уже существующую базу данных Мирокод может вызвать несколько проблем. В большинстве случаев вы должны использовать существующий "app.ini" для запуска Мирокод. Если вы понимаете, что вы делаете, подтвердите: reinstall_confirm_check_1=Данные, зашифрованные SECRET_KEY в приложении, могут быть потеряны: пользователи не смогут войти в систему с помощью 2FA/OTP & зеркала могут работать неправильно. Отметьте этот флажок, чтобы убедиться, что текущий файл app.ini содержит корректный SECRET_KEY. reinstall_confirm_check_2=Репозитории и настройки могут понадобиться пересинхронизировать. Отметьте этот флажок, чтобы вручную синхронизировать хуки для репозиториев и authorized_keys. Вы подтверждаете, что настройки репозитория и зеркала верны. -reinstall_confirm_check_3=Вы подтверждаете, что полностью уверены, что этот Gitea запущен при коренном расположении app.ini и вы уверены, что вам нужна переустановка. Вы подтверждаете, что соглашаетесь с указанными выше рисками. +reinstall_confirm_check_3=Вы подтверждаете, что полностью уверены, что этот Мирокод запущен при коренном расположении app.ini и вы уверены, что вам нужна переустановка. Вы подтверждаете, что соглашаетесь с указанными выше рисками. err_empty_db_path=Путь к базе данных SQLite3 не может быть пустым. no_admin_and_disable_registration=Вы не можете отключить регистрацию до создания учётной записи администратора. err_empty_admin_password=Пароль администратора не может быть пустым. @@ -155,14 +156,14 @@ repo_path_helper=Все удалённые Git репозитории будут lfs_path=Корневой путь Git LFS lfs_path_helper=В этой директории будут храниться файлы Git LFS. Оставьте пустым, чтобы отключить LFS. run_user=Запуск от имени пользователя -run_user_helper=Введите имя пользователя операционной системы, под которым работает Gitea. Обратите внимание, что этот пользователь должен иметь доступ к корневому пути репозиториев. +run_user_helper=Введите имя пользователя операционной системы, под которым работает Мирокод. Обратите внимание, что этот пользователь должен иметь доступ к корневому пути репозиториев. domain=Домен сервера domain_helper=Домен или адрес хоста для сервера. ssh_port=Порт SSH сервера ssh_port_helper=Номер порта, который использует SSH сервер. Оставьте пустым, чтобы отключить SSH. -http_port=Gitea HTTP порт -http_port_helper=Номер порта, который будет прослушиваться Gitea веб-сервером. -app_url=Базовый URL-адрес Gitea +http_port=Мирокод HTTP порт +http_port_helper=Номер порта, который будет прослушиваться Мирокод веб-сервером. +app_url=Базовый URL-адрес Мирокод app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на некоторые уведомления по электронной почте. log_root_path=Путь к журналу log_root_path_helper=Файлы журнала будут записываться в этот каталог. @@ -171,7 +172,7 @@ optional_title=Расширенные настройки email_title=Настройки электронной почты smtp_host=Узел SMTP smtp_from=Отправить эл. почту как -smtp_from_helper=Адрес электронной почты, который будет использоваться Gitea. Введите обычный адрес электронной почты или используйте формат "Имя" . +smtp_from_helper=Адрес электронной почты, который будет использоваться Мирокод. Введите обычный адрес электронной почты или используйте формат "Имя" . mailer_user=SMTP логин mailer_password=SMTP пароль register_confirm=Требовать подтверждение по электронной почте для регистрации @@ -200,9 +201,9 @@ admin_name=Логин администратора admin_password=Пароль confirm_password=Подтвердить пароль admin_email=Адрес эл. почты -install_btn_confirm=Установить Gitea +install_btn_confirm=Установить Мирокод test_git_failed=Не удалось проверить 'git' команду: %v -sqlite3_not_available=Эта версия Gitea не поддерживает SQLite3. Пожалуйста, загрузите официальную бинарную версию из %s (не 'go build' версия). +sqlite3_not_available=Эта версия Мирокод не поддерживает SQLite3. Пожалуйста, загрузите официальную бинарную версию из %s (не 'go build' версия). invalid_db_setting=Некорректные настройки базы данных: %v invalid_db_table=Таблица базы данных '%s' недействительна: %v invalid_repo_path=Недопустимый путь к корню репозитория: %v @@ -212,12 +213,12 @@ internal_token_failed=Не удалось создать внутренний т secret_key_failed=Не удалось создать секретный ключ: %v save_config_failed=Не удалось сохранить конфигурацию: %v invalid_admin_setting=Некорректные настройки учётной записи администратора: %v -install_success=Добро пожаловать! Благодарим вас за выбор Gitea, пользуйтесь с удовольствием! +install_success=Добро пожаловать! Благодарим вас за выбор Мирокод, пользуйтесь с удовольствием! invalid_log_root_path=Недопустимый путь для логов: %v default_keep_email_private=Скрывать адреса электронной почты по умолчанию default_keep_email_private_popup=Скрывать адреса электронной почты новых учетных записей по умолчанию. -default_allow_create_organization=Разрешить создание организаций по умолчанию -default_allow_create_organization_popup=Разрешить новым учетным записям пользователей создавать организации по умолчанию. +default_allow_create_organization=Разрешить создание сообществ по умолчанию +default_allow_create_organization_popup=Разрешить новым учетным записям пользователей создавать сообщества по умолчанию. default_enable_timetracking=Включить отслеживание времени по умолчанию default_enable_timetracking_popup=Включить отслеживание времени для новых репозиториев по умолчанию. no_reply_address=Скрытый почтовый домен @@ -229,15 +230,15 @@ password_algorithm_helper=Задайте алгоритм хеширования uname_holder=Имя пользователя / Адрес эл. почты password_holder=Пароль switch_dashboard_context=Переключить контекст панели управления -my_repos=Репозитории +my_repos=Проекты show_more_repos=Показать больше репозиториев… -collaborative_repos=Совместные репозитории -my_orgs=Мои организации +collaborative_repos=Совместные проекты +my_orgs=Мои сообщества my_mirrors=Мои зеркала view_home=Показать %s -search_repos=Поиск репозитория… +search_repos=Поиск проекта… filter=Другие фильтры -filter_by_team_repositories=Фильтровать по репозиториям команды +filter_by_team_repositories=Фильтровать по проектам команды feed_of=Лента "%s" show_archived=Архивировано @@ -250,19 +251,19 @@ show_both_private_public=Показаны как публичные, так и show_only_private=Показаны только приватные show_only_public=Показаны только публичные -issues.in_your_repos=В ваших репозиториях +issues.in_your_repos=В ваших проектах [explore] -repos=Репозитории +repos=Проекты users=Пользователи -organizations=Организации +organizations=Сообщества search=Поиск -code=Код +code=Файлы search.fuzzy=Неточный search.match=Соответствие -repo_no_results=Подходящие репозитории не найдены. +repo_no_results=Подходящие проекты не найдены. user_no_results=Подходящие пользователи не найдены. -org_no_results=Подходящие организации не найдены. +org_no_results=Подходящие сообщества не найдены. code_no_results=Соответствующий поисковому запросу исходный код не найден. code_search_results=Результаты поиска для '%s' code_last_indexed_at=Последний проиндексированный %s @@ -296,7 +297,7 @@ invalid_code=Код подтверждения недействителен ил reset_password_helper=Восстановить аккаунт reset_password_wrong_user=Вы вошли как %s, но ссылка для восстановления аккаунта %s password_too_short=Пароль не может быть короче %d символов. -non_local_account=Нелокальные аккаунты не могут изменить пароль через Gitea. +non_local_account=Нелокальные аккаунты не могут изменить пароль через Мирокод. verify=Проверить scratch_code=Одноразовый пароль use_scratch_code=Использовать scratch-код @@ -323,7 +324,7 @@ email_domain_blacklisted=С данным адресом электронной authorize_application=Авторизация приложения authorize_redirect_notice=Вы будете перенаправлены на %s, если вы авторизуете это приложение. authorize_application_created_by=Это приложение было создано %s. -authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных репозиториев и организаций. +authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных проектов и сообществ. authorize_title=Разрешить "%s" доступ к вашей учётной записи? authorization_failed=Ошибка авторизации authorization_failed_desc=Ошибка авторизации, обнаружен неверный запрос. Пожалуйста, свяжитесь с автором приложения, которое вы пытались авторизовать. @@ -345,7 +346,7 @@ activate_email=Подтвердите адрес своей электронно activate_email.title=%s, пожалуйста, подтвердите ваш адрес электронной почты activate_email.text=Пожалуйста, перейдите по ссылке, чтобы подтвердить ваш адрес электронной почты в течение %s: -register_notify=Добро пожаловать на Gitea +register_notify=Добро пожаловать на Мирокод register_notify.title=%[1]s, добро пожаловать в %[2]s register_notify.text_1=это письмо с вашим подтверждением регистрации в %s! register_notify.text_2=Теперь вы можете войти через логин: %s. @@ -389,7 +390,7 @@ repo.transfer.to_you=вам repo.transfer.body=Для того чтобы принять или отклонить перейдите по ссылке %s или просто проигнорируйте данный запрос. repo.collaborator.added.subject=%s добавил вас в %s -repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория: +repo.collaborator.added.text=Вы были добавлены в качестве соавтора проекта: [modal] yes=Да @@ -398,7 +399,7 @@ modify=Изменить [form] UserName=Пользователь -RepoName=Название репозитория +RepoName=Название проекта Email=Адрес эл. почты Password=Пароль Retype=Введите пароль еще раз @@ -438,17 +439,17 @@ lang_select_error=Выберите язык из списка. username_been_taken=Имя пользователя уже занято. username_change_not_local_user=Нелокальным пользователям запрещено изменять их имя пользователя. -repo_name_been_taken=Название репозитория уже используется. -repository_force_private=Включена принудительная приватность: приватные репозитории не могут быть сделаны публичными. -repository_files_already_exist=Файлы уже существуют для этого репозитория. Обратитесь к системному администратору. -repository_files_already_exist.adopt=Файлы уже существуют для этого репозитория и могут быть только приняты. -repository_files_already_exist.delete=Файлы уже существуют для этого репозитория. Вы должны удалить их. -repository_files_already_exist.adopt_or_delete=Файлы уже существуют для этого репозитория. Или принять их или удалить их. +repo_name_been_taken=Название проекта уже используется. +repository_force_private=Включена принудительная приватность: приватные проекты не могут быть сделаны публичными. +repository_files_already_exist=Файлы уже существуют для этого проекта. Обратитесь к системному администратору. +repository_files_already_exist.adopt=Файлы уже существуют для этого проекта и могут быть только приняты. +repository_files_already_exist.delete=Файлы уже существуют для этого проекта. Вы должны удалить их. +repository_files_already_exist.adopt_or_delete=Файлы уже существуют для этого проекта. Или принять их или удалить их. visit_rate_limit=Удалённый вход отклонён в связи с ограничением количества попыток в секунду. 2fa_auth_required=Удалённый вход требует двух-факторную аутентификацию. -org_name_been_taken=Название организации уже занято. +org_name_been_taken=Название сообщества уже занято. team_name_been_taken=Название команды уже занято. -team_no_units_error=Разрешите доступ хотя бы к одному разделу репозитория. +team_no_units_error=Разрешите доступ хотя бы к одному разделу проекта. email_been_used=Этот адрес электронной почты уже используется. email_invalid=Недопустимый адрес электронной почты. openid_been_used=Адрес OpenID '%s' уже используется. @@ -458,14 +459,14 @@ password_lowercase_one=Как минимум один строчный симв password_uppercase_one=Как минимум один заглавный символ password_digit_one=По крайней мере одна цифра password_special_one=По крайней мере один специальный символ (знаки пунктуации, скобки, кавычки и т. д.) -enterred_invalid_repo_name=Введённое вами название репозитория неверно. -enterred_invalid_org_name=Введённое вам название организации некорректно. +enterred_invalid_repo_name=Введённое вами название проекта неверно. +enterred_invalid_org_name=Введённое вам название сообщества некорректно. enterred_invalid_owner_name=Имя нового владельца некорректно. enterred_invalid_password=Введённый пароль неверен. user_not_exist=Пользователь не существует. team_not_exist=Команда не существует. -last_org_owner=Вы не можете удалить последнего пользователя из команды 'Владельцы'. Для организации должен быть хотя бы один владелец. -cannot_add_org_to_team=Организацию нельзя добавить в качестве члена команды. +last_org_owner=Вы не можете удалить последнего пользователя из команды 'Владельцы'. Для сообщества должен быть хотя бы один владелец. +cannot_add_org_to_team=Сообщество нельзя добавить в качестве члена команды. invalid_ssh_key=Не удается проверить SSH ключ: %s invalid_gpg_key=Не удается проверить GPG ключ: %s @@ -474,20 +475,20 @@ unable_verify_ssh_key=Не удаётся проверить ключ SSH; пе auth_failed=Ошибка аутентификации: %v still_own_repo=Ваша учётная запись владеет одним или несколькими репозиториями; сначала удалите или перенесите их. -still_has_org=Ваша учётная запись является членом одной или нескольких организаций; сначала выйдите из них. -org_still_own_repo=Эта организация по-прежнему владеет одним или несколькими репозиториями; сначала удалите или перенесите их. +still_has_org=Ваша учётная запись является членом одной или нескольких сообществ; сначала выйдите из них. +org_still_own_repo=Эта сообщество по-прежнему владеет одним или несколькими репозиториями; сначала удалите или перенесите их. target_branch_not_exist=Целевая ветка не существует. [user] change_avatar=Изменить свой аватар… join_on=Присоединился(-ась) -repositories=Репозитории +repositories=Проекты activity=Активность followers=Подписчики -starred=Избранные репозитории -watched=Отслеживаемые репозитории -projects=Проекты +starred=Избранные проекты +watched=Отслеживаемые проекты +projects=Направления following=Подписки follow=Подписаться unfollow=Отписаться @@ -509,12 +510,12 @@ avatar=Аватар ssh_gpg_keys=SSH / GPG ключи social=Учётные записи в соцсетях applications=Приложения -orgs=Управление организациями -repos=Репозитории +orgs=Управление сообществами +repos=Проекты delete=Удалить аккаунт twofa=Двухфакторная аутентификация account_link=Привязанные аккаунты -organization=Организации +organization=Сообщества uid=UID public_profile=Открытый профиль @@ -524,6 +525,7 @@ password_username_disabled=Нелокальным пользователям з full_name=Имя и фамилия website=Веб-сайт location=Местоположение +locationcoordinate=Координаты update_theme=Обновить тему update_profile=Обновить профиль update_language=Обновить язык @@ -558,7 +560,7 @@ new_password=Новый пароль retype_new_password=Подтверждение нового пароля password_incorrect=Текущий пароль неправильный. change_password_success=Ваш пароль был изменён. С этого момента необходимо использовать новый пароль для входа. -password_change_disabled=Нелокальные аккаунты не могут изменить пароль через Gitea. +password_change_disabled=Нелокальные аккаунты не могут изменить пароль через Мирокод. emails=Email адреса manage_emails=Управление адресами электронной почты @@ -669,13 +671,13 @@ hide_openid=Скрыть из профиля ssh_disabled=SSH отключён ssh_externally_managed=Этот SSH ключ управляется извне для этого пользователя manage_social=Управление привязанными учетными записями в соцсетях -social_desc=Эти социальные сети связаны с вашим аккаунтом Gitea. Их можно использовать для входа в учетную запись Gitea, поэтому необходимо быть уверенным в том, что никаких посторонних аккаунтов не подключено. +social_desc=Эти социальные сети связаны с вашим аккаунтом Мирокод. Их можно использовать для входа в учетную запись Мирокод, поэтому необходимо быть уверенным в том, что никаких посторонних аккаунтов не подключено. unbind=Удалить связь unbind_success=Связанная внешняя учётная запись была удалена. manage_access_token=Управление токенами generate_new_token=Создать новый токен -tokens_desc=Эти токены предоставляют доступ к вашей учетной записи с помощью Gitea API. +tokens_desc=Эти токены предоставляют доступ к вашей учетной записи с помощью Мирокод API. new_token_desc=Приложения, использующие токен, имеют полный доступ к вашей учетной записи. token_name=Имя токена generate_token=Генерировать токен @@ -688,7 +690,7 @@ delete_token_success=Токен удалён. Приложения, исполь manage_oauth2_applications=Управление приложениями OAuth2 edit_oauth2_application=Изменить OAuth2 приложение -oauth2_applications_desc=Приложения OAuth2 позволяет стороннему приложению к безопасно аутентифицировать пользователей данной установки Gitea. +oauth2_applications_desc=Приложения OAuth2 позволяет стороннему приложению к безопасно аутентифицировать пользователей данной установки Мирокод. remove_oauth2_application=Удалить OAuth2 приложение remove_oauth2_application_desc=Удаление приложения OAuth2 отменит доступ ко всем подписанным токенам доступа. Продолжить? remove_oauth2_application_success=Приложение было удалено. @@ -712,7 +714,7 @@ oauth2_application_create_description=Приложения OAuth2 предост oauth2_application_remove_description=Удаление приложения OAuth2 приведёт к отмене его доступа к авторизованным учётным записям пользователей в данном экземпляре. Продолжить? authorized_oauth2_applications=Авторизованные приложения OAuth2 -authorized_oauth2_applications_description=Вы предоставили доступ к вашему персональному аккаунту Gitea этим сторонним приложениям. Пожалуйста, отзовите доступ у приложений, которые больше не используются. +authorized_oauth2_applications_description=Вы предоставили доступ к вашему персональному аккаунту Мирокод этим сторонним приложениям. Пожалуйста, отзовите доступ у приложений, которые больше не используются. revoke_key=Отозвать revoke_oauth2_grant=Отозвать доступ revoke_oauth2_grant_description=Отзыв доступа у этого стороннего приложения не позволит ему получать доступ к вашим данным. Вы уверены? @@ -739,14 +741,14 @@ twofa_failed_get_secret=Не удалось получить ключ. webauthn_desc=Ключи безопасности - это аппаратные устройства, содержащие криптографические ключи. Они могут использоваться для двухфакторной аутентификации. Ключи безопасности должны поддерживать стандарт WebAuthn Authenticator. manage_account_links=Управление привязанными аккаунтами -manage_account_links_desc=Эти внешние аккаунты привязаны к вашему аккаунту Gitea. -account_links_not_available=В настоящее время нет внешних аккаунтов, привязанных к вашему аккаунту Gitea. +manage_account_links_desc=Эти внешние аккаунты привязаны к вашему аккаунту Мирокод. +account_links_not_available=В настоящее время нет внешних аккаунтов, привязанных к вашему аккаунту Мирокод. remove_account_link=Удалить привязанный аккаунт -remove_account_link_desc=Удаление привязанной учетной записи отменит её доступ к вашей учетной записи Gitea. Продолжить? +remove_account_link_desc=Удаление привязанной учетной записи отменит её доступ к вашей учетной записи Мирокод. Продолжить? remove_account_link_success=Привязанная учетная запись удалена. -orgs_none=Вы не состоите ни в одной организации. -repos_none=Вы не владеете репозиториями +orgs_none=Вы не состоите ни в одной сообщества. +repos_none=Вы не владеете проектами delete_account=Удалить свой аккаунт delete_prompt=Эта операция навсегда удалит вашу учетную запись. Это НЕВОЗМОЖНО будет отменить. @@ -766,23 +768,23 @@ visibility.public_tooltip=Видимый для всех пользовател visibility.limited=Ограниченный visibility.limited_tooltip=Видимый только авторизованным пользователям visibility.private=Приватный -visibility.private_tooltip=Видимый только членам организации +visibility.private_tooltip=Видимый только членам сообщества [repo] new_repo_helper=Репозиторий содержит все файлы проекта, включая историю ревизии. Уже есть где-то еще? Мигрировать репозиторий. owner=Владелец -owner_helper=Некоторые организации могут не отображаться в раскрывающемся списке из-за максимального ограничения количества репозиториев. -repo_name=Название репозитория +owner_helper=Некоторые сообщества могут не отображаться в раскрывающемся списке из-за максимального ограничения количества проектов. +repo_name=Название проекта repo_name_helper=Лучшие названия репозиториев состоят из коротких, легко запоминаемых и уникальных ключевых слов. -repo_size=Размер репозитория +repo_size=Размер проекта template=Шаблон template_select=Выбрать шаблон. -template_helper=Сделать репозиторий шаблоном -template_description=Шаблонные репозитории дают возможность пользователям создавать новые репозитории с той же структурой каталогов, файлами и дополнительными настройками. +template_helper=Сделать проект шаблоном +template_description=Шаблонные проекты дают возможность пользователям создавать новые проекты с той же структурой каталогов, файлами и дополнительными настройками. visibility=Видимость -visibility_description=Только владелец или члены организации, при наличии прав, смогут увидеть это. -visibility_helper=Сделать репозиторий частным -visibility_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию. +visibility_description=Только владелец или члены сообщества, при наличии прав, смогут увидеть это. +visibility_helper=Сделать проект частным +visibility_helper_forced=Администратор сайта настроил параметр видимости новых проектов. Проект приватный по умолчанию. visibility_fork_helper=(Изменение этого повлияет на все форки.) clone_helper=Нужна помощь в клонировании? Посетите страницу помощи. fork_repo=Форкнуть репозиторий @@ -793,7 +795,7 @@ clone_in_vsc=Клонировать в VS Code download_zip=Скачать ZIP download_tar=Скачать TAR.GZ download_bundle=Скачать BUNDLE -generate_repo=Создать репозиторий +generate_repo=Создать проект generate_from=Создать из repo_desc=Описание repo_desc_helper=Добавьте краткое описание (необязательно) @@ -814,7 +816,7 @@ trust_model_helper_collaborator=Соавтор: Подписи доверия о trust_model_helper_committer=Участник: доверенные подписи участников trust_model_helper_collaborator_committer=Соавтор+Коммитер: Доверять подписи соавторам, которые соответствуют коммитеру trust_model_helper_default=По умолчанию: используйте модель доверия по умолчанию для этой установки -create_repo=Создать репозиторий +create_repo=Создать проект default_branch=Ветка по умолчанию default_branch_helper=Ветка по умолчанию является базовой веткой для запросов на слияние и коммитов кода. mirror_prune=Очистить @@ -838,13 +840,13 @@ stargazers=Звездочеты forks=Форки pick_reaction=Оставьте свою оценку! reactions_more=и ещё %d -unit_disabled=Администратор сайта отключил этот раздел репозитория. +unit_disabled=Администратор сайта отключил этот раздел проекта. language_other=Разное -adopt_search=Введите имя пользователя для поиска неутверждённых репозиториев... (оставьте пустым, чтобы найти все) +adopt_search=Введите имя пользователя для поиска неутверждённых проектов... (оставьте пустым, чтобы найти все) adopt_preexisting_label=Принятые файлы adopt_preexisting=Принять уже существующие файлы -adopt_preexisting_content=Создать репозиторий из %s -adopt_preexisting_success=Приняты файлы и создан репозиторий из %s +adopt_preexisting_content=Создать проект из %s +adopt_preexisting_success=Приняты файлы и создан проект из %s delete_preexisting_label=Удалить delete_preexisting=Удалить уже существующие файлы delete_preexisting_content=Удалить файлы из %s @@ -881,15 +883,15 @@ archive.title=Это репозиторий в архиве. Вы можете archive.issue.nocomment=Этот репозиторий в архиве. Вы не можете комментировать задачи. archive.pull.nocomment=Это репозиторий в архиве. Вы не можете комментировать запросы на слияние. -form.reach_limit_of_creation_1=Достигнуто ограничение на количество репозиториев: %d. -form.reach_limit_of_creation_n=Достигнуто ограничение на количество репозиториев: %d. -form.name_reserved=Название репозитория '%s' зарезервировано. -form.name_pattern_not_allowed=Шаблон имени репозитория '%s' не допускается. +form.reach_limit_of_creation_1=Достигнуто ограничение на количество проектов: %d. +form.reach_limit_of_creation_n=Достигнуто ограничение на количество проектов: %d. +form.name_reserved=Название проекта '%s' зарезервировано. +form.name_pattern_not_allowed=Шаблон имени проекта '%s' не допускается. need_auth=Авторизация migrate_options=Параметры миграции migrate_service=Сервис миграции -migrate_options_mirror_helper=Этот репозиторий будет зеркалом +migrate_options_mirror_helper=Этот проект будет зеркалом migrate_options_mirror_disabled=Администратор вашего сайта отключил новые зеркала. migrate_options_lfs=Перенос LFS файлов migrate_options_lfs_endpoint.label=LFS Endpoint @@ -904,7 +906,7 @@ migrate_items_issues=Задачи migrate_items_pullrequests=Запросы на слияние migrate_items_merge_requests=Запросы на слияние migrate_items_releases=Релизы -migrate_repo=Перенос репозитория +migrate_repo=Перенос проекта migrate.clone_address=Перенос / Клонирование по URL migrate.clone_address_desc=Это может быть HTTP/HTTPS/GIT адрес или локальный путь существующего репозитория на сервере. migrate.clone_local_path=или локальный путь на сервере @@ -923,7 +925,7 @@ migrate.migrating_failed.error=Ошибка: %s migrate.migrating_failed_no_addr=Миграция не удалась. migrate.git.description=Перенести только репозиторий из любого Git сервиса. migrate.gitlab.description=Перенести данные с gitlab.com или других экземпляров GitLab. -migrate.gitea.description=Перенести данные с gitea.com или других экземпляров Gitea. +migrate.gitea.description=Перенести данные с gitea.com или других экземпляров Мирокод. migrate.gogs.description=Перенести данные с notabug.org или других экземпляров Gogs. migrate.onedev.description=Перенести данные с code.onedev.io или других экземпляров OneDev. migrate.codebase.description=Перенос данных с codebasehq.com. @@ -942,23 +944,23 @@ generated_from=создано из fork_from_self=Вы не можете форкнуть ваш собственный репозиторий. fork_guest_user=Войдите, чтобы форкнуть репозиторий. watch_guest_user=Войдите, чтобы следить за этим репозиторием. -star_guest_user=Войдите, чтобы добавить в избранное этот репозиторий. +star_guest_user=Войдите, чтобы добавить в избранное этот проект. unwatch=Перестать следить watch=Следить unstar=Убрать из избранного star=В избранное fork=Форкнуть -download_archive=Скачать репозиторий +download_archive=Скачать проект no_desc=Нет описания quick_guide=Краткое руководство -clone_this_repo=Клонировать репозиторий -create_new_repo_command=Создать новый репозиторий из командной строки -push_exist_repo=Push существующего репозитория из командной строки +clone_this_repo=Клонировать проект +create_new_repo_command=Создать новый проект из командной строки +push_exist_repo=Push существующего проекта из командной строки empty_message=В репозитории нет файлов. -code=Код -code.desc=Исходный код, файлы, коммиты и ветки. +code=Файлы +code.desc=Файлы, коммиты и ветки. branch=ветка tree=Дерево clear_ref=`Удалить текущую ссылку` @@ -968,9 +970,9 @@ branches=Ветки tags=Теги issues=Задачи pulls=Запросы на слияние -project_board=Проекты +project_board=Направления labels=Метки -org_labels_desc=Метки уровня организации, которые можно использовать с всеми репозиториями< / strong> в этой организации +org_labels_desc=Метки уровня сообщества, которые можно использовать с всеми репозиториями< / strong> в этом сообществе org_labels_desc_manage=управлять milestones=Этапы @@ -989,6 +991,8 @@ file_view_raw=Посмотреть исходник file_permalink=Постоянная ссылка file_too_large=Этот файл слишком большой, поэтому он не может быть отображён. +issues_tree=Дерево задач + file_copy_permalink=Копировать постоянную ссылку video_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'video' тэг. audio_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'audio' тэг. @@ -1082,27 +1086,27 @@ commits.gpg_key_id=Идентификатор GPG ключа ext_issues.desc=Ссылка на внешнюю систему отслеживания ошибок. -projects=Проекты +projects=Направления projects.desc=Управление задачами и pull'ами в досках проекта. projects.description=Описание (необязательно) projects.description_placeholder=Описание -projects.create=Создать проект +projects.create=Создать направление projects.title=Заголовок -projects.new=Новый проект +projects.new=Новое направление projects.new_subheader=Координируйте, отслеживайте и обновляйте работу в одном месте, так что проекты остаются прозрачными и в графике. -projects.create_success=Проект '%s' был создан. -projects.deletion=Удалить проект -projects.deletion_desc=Удаление проекта приведёт к его удалению из всех связанных задач. Продолжить? -projects.deletion_success=Проект был удалён. -projects.edit=Редактировать проекты +projects.create_success=Направление '%s' был создан. +projects.deletion=Удалить направление +projects.deletion_desc=Удаление направления приведёт к его удалению из всех связанных задач. Продолжить? +projects.deletion_success=Направление было удалено. +projects.edit=Редактировать направления projects.edit_subheader=Создавайте и организуйте задачи и отслеживайте прогресс. -projects.modify=Обновить проект +projects.modify=Обновить направление projects.edit_success=Проект '%s' был обновлён. projects.type.none=Нет projects.type.basic_kanban=Обычный Канбан projects.type.bug_triage=Планирование работы с багами -projects.template.desc=Шаблон проекта -projects.template.desc_helper=Выберите шаблон проекта для начала +projects.template.desc=Шаблон направления +projects.template.desc_helper=Выберите шаблон направления для начала projects.type.uncategorized=Без категории projects.board.edit=Редактировать доску projects.board.edit_title=Новое имя доски @@ -1112,7 +1116,7 @@ projects.board.new=Новая доска projects.board.set_default=Установить по умолчанию projects.board.set_default_desc=Установить эту доску по умолчанию для неклассифицированных задач и pull-ов projects.board.delete=Удалить доску -projects.board.deletion_desc=Удаление доски проектов перемещает все связанные задачи в 'Без категории'. Продолжить? +projects.board.deletion_desc=Удаление доски направления перемещает все связанные задачи в 'Без категории'. Продолжить? projects.board.color=Цвет projects.open=Открыть projects.close=Закрыть @@ -1120,7 +1124,7 @@ projects.close=Закрыть issues.desc=Организация отчетов об ошибках, задач и этапов. issues.filter_assignees=Фильтр назначений issues.filter_milestones=Фильтр этапов -issues.filter_projects=Фильтровать проекты +issues.filter_projects=Фильтровать направления issues.filter_labels=Фильтр меток issues.filter_reviewers=Фильтр рецензентов issues.new=Новая задача @@ -1129,12 +1133,12 @@ issues.new.labels=Метки issues.new.add_labels_title=Применить метки issues.new.no_label=Нет меток issues.new.clear_labels=Очистить метки -issues.new.projects=Проекты -issues.new.add_project_title=Задать проект -issues.new.clear_projects=Очистить проекты -issues.new.no_projects=Нет проекта -issues.new.open_projects=Открытые проекты -issues.new.closed_projects=Закрытые проекты +issues.new.projects=Направления +issues.new.add_project_title=Задать направление +issues.new.clear_projects=Очистить направления +issues.new.no_projects=Нет направления +issues.new.open_projects=Открытые направления +issues.new.closed_projects=Закрытые направления issues.new.no_items=Нет элементов issues.new.milestone=Этап issues.new.add_milestone_title=Установить этап @@ -1168,11 +1172,11 @@ issues.remove_label=удалён %s с меткой %s issues.remove_labels=удалён %s с метками %s issues.add_remove_labels=добавлен %s и удалён %s с метками %s issues.add_milestone_at=`добавил(а) к этапу %s %s` -issues.add_project_at=`добавил(а) в %s проект %s` +issues.add_project_at=`добавил(а) в %s направление %s` issues.change_milestone_at=`поменял(а) целевой этап с %s на %s %s` -issues.change_project_at=`изменил(а) проект с %s на %s %s` +issues.change_project_at=`изменил(а) направление с %s на %s %s` issues.remove_milestone_at=`удалил(а) из этапа %s %s` -issues.remove_project_at=`удалил(а) это из проекта %s %s` +issues.remove_project_at=`удалил(а) это из направления %s %s` issues.deleted_milestone=`(удалено)` issues.deleted_project=`(удалено)` issues.self_assign_at=`назначил(а) на себя %s` @@ -1296,7 +1300,7 @@ issues.unlock_comment=снял(а) ограничение %s issues.lock_confirm=Ограничить issues.unlock_confirm=Снять issues.lock.notice_1=- Другие пользователи не могут добавлять новые комментарии к этой задаче. -issues.lock.notice_2=- Вы и другие соавторы с доступом к этому репозиторию могут оставлять комментарии, которые могут видеть другие. +issues.lock.notice_2=- Вы и другие соавторы с доступом к этому проекту могут оставлять комментарии, которые могут видеть другие. issues.lock.notice_3=- Вы всегда можете снять ограничение с обсуждения этой задачи. issues.unlock.notice_1=- Все снова смогут принять участие в обсуждении данной задачи. issues.unlock.notice_2=- Вы всегда можете снова наложить ограничение на обсуждение этой задачи. @@ -1369,7 +1373,34 @@ issues.dependency.add_error_dep_issue_not_exist=Зависимая задача issues.dependency.add_error_dep_not_exist=Зависимости не существует. issues.dependency.add_error_dep_exists=Зависимость уже существует. issues.dependency.add_error_cannot_create_circular=Вы не можете создать зависимость с двумя задачами, блокирующими друг друга. -issues.dependency.add_error_dep_not_same_repo=Обе задачи должны находиться в одном репозитории. +issues.dependency.add_error_dep_not_same_repo=Обе задачи должны находиться в одном проекте. + +issues.parent.title=Родительские задачи +issues.parent.issue_no_parents=В настоящее время эта задача не имеет родителей. +issues.parent.pr_no_parents=Этот запрос на слияние в настоящее время не имеет никаких родителей. +issues.parent.add=Добавить родителя… +issues.parent.cancel=Отменить +issues.parent.remove=Удалить +issues.parent.remove_info=Удалить этого родителя +issues.parent.added_parent=`добавить нового родителя %s` +issues.parent.removed_parent=`убрал родителя %s` +issues.parent.pr_closing_blockedby=Этот запрос на слияние имеет родителей +issues.parent.issue_closing_blockedby=Эта задача имеет родителей +issues.parent.issue_close_blocks=Эта задача имеет следующих детей +issues.parent.pr_close_blocks=Этот запрос на слияние имеет следующих детей +issues.parent.blocks_short=Дети +issues.parent.blocked_by_short=Родители +issues.parent.remove_header=Удалить родителя +issues.parent.issue_remove_text=Это приведет к удалению родителя от этой задачи. Продолжить? +issues.parent.pr_remove_text=Это приведёт к удалению родителя от этого запроса на слияние. Продолжить? +issues.parent.setting=Включение родителя для задач и запросов на слияние +issues.parent.add_error_same_issue=Вы не можете указать родителя задачи на саму себя. +issues.parent.add_error_dep_issue_not_exist=Родительсткая задача не существует. +issues.parent.add_error_dep_not_exist=Родительсткой задачи не существует. +issues.parent.add_error_dep_exists=Привязка к родителю уже существует. +issues.parent.add_error_cannot_create_circular=Вы не можете создать родителей с двумя задачами, блокирующими друг друга. +issues.parent.add_error_dep_not_same_repo=Обе задачи должны находиться в одном проекте. + issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние. issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние. issues.review.approve=одобрил(а) эти изменения %s @@ -1504,8 +1535,8 @@ pulls.closed_at=`закрыл этот запрос на слияние %[2]s` pulls.merge_instruction_hint=`Вы также можете просмотреть инструкции командной строки.` -pulls.merge_instruction_step1_desc=В репозитории вашего проекта посмотрите новую ветку и протестируйте изменения. -pulls.merge_instruction_step2_desc=Объединить изменения и обновить на Gitea. +pulls.merge_instruction_step1_desc=В проекте вашего направления посмотрите новую ветку и протестируйте изменения. +pulls.merge_instruction_step2_desc=Объединить изменения и обновить на Мирокод. milestones.new=Новый этап milestones.open_tab=%d открыто(ы) @@ -1553,31 +1584,31 @@ signing.wont_sign.commitssigned=Слияние не будет подписан signing.wont_sign.approved=Слияние не будет подписано, так как PR не одобрен signing.wont_sign.not_signed_in=Вы не авторизовались -ext_wiki.desc=Ссылка на внешнюю вики. +ext_wiki.desc=Ссылка на внешнее описание. -wiki=Вики -wiki.welcome=Добро пожаловать в Вики. -wiki.welcome_desc=Вики позволяет писать и делиться документацией с сотрудниками. -wiki.desc=Вики - это место для хранения документации. +wiki=Описание проекта +wiki.welcome=Добро пожаловать в описание проекта. +wiki.welcome_desc=Описание проекта позволяет делиться документацией с сотрудниками. +wiki.desc=В описании проекта храниться вся документация по нему. wiki.create_first_page=Создать первую страницу wiki.page=Страница wiki.filter_page=Фильтр страницы wiki.new_page=Страница -wiki.default_commit_message=Описание изменения вики-страницы (необязательно). +wiki.default_commit_message=Изменения описания проекта (необязательно). wiki.save_page=Сохранить страницу wiki.last_commit_info=%s редактировал(а) эту страницу %s wiki.edit_page_button=Редактировать wiki.new_page_button=Новая страница wiki.file_revision=Версия страницы -wiki.wiki_page_revisions=Версии Вики-страниц -wiki.back_to_wiki=Вернуться на вики страницу +wiki.wiki_page_revisions=Версии описания проекта +wiki.back_to_wiki=Вернуться на описание проекта wiki.delete_page_button=Удалить страницу -wiki.delete_page_notice_1=Удаление вики-страницы '%s' не может быть отменено. Продолжить? -wiki.page_already_exists=Вики-страница с таким именем уже существует. -wiki.reserved_page=Имя вики-страницы '%s' зарезервировано. +wiki.delete_page_notice_1=Удаление описания направления '%s' не может быть отменено. Продолжить? +wiki.page_already_exists=Описание направления с таким именем уже существует. +wiki.reserved_page=Имя описания направления '%s' зарезервировано. wiki.pages=Страницы wiki.last_updated=Последнее обновление %s -wiki.page_name_desc=Введите имя страницы Вики. Некоторые специальные имена: 'Главна', '_Sidebar' и '_Footer'. +wiki.page_name_desc=Введите имя страницы описания. Некоторые специальные имена: 'Главна', '_Sidebar' и '_Footer'. activity=Активность activity.period.filter_label=Период: @@ -1646,14 +1677,14 @@ activity.git_stats_deletion_1=%d удаление activity.git_stats_deletion_n=%d удалений search=Поиск -search.search_repo=Поиск по репозиторию +search.search_repo=Поиск по проекту search.fuzzy=Неточный search.match=Соответствие search.results=Результаты поиска "%s" в %s settings=Настройки -settings.desc=В настройках вы можете менять различные параметры этого репозитория -settings.options=Репозиторий +settings.desc=В настройках вы можете менять различные параметры этого проекта +settings.options=Проект settings.collaboration=Соавторы settings.collaboration.admin=Администратор settings.collaboration.write=Запись @@ -1664,7 +1695,7 @@ settings.hooks=Веб-хуки settings.githooks=Git Hook'и settings.basic_settings=Основные параметры settings.mirror_settings=Настройки зеркалирования -settings.mirror_settings.docs=Настройте свой проект, чтобы автоматически отправлять и/или получать изменения из другого репозитория. Ветки, теги и коммиты будут синхронизированы автоматически. Как мне зеркалировать репозитории? +settings.mirror_settings.docs=Настройте своё направление, чтобы автоматически отправлять и/или получать изменения из другого проекта. Ветки, теги и коммиты будут синхронизированы автоматически. Как мне зеркалировать репозитории? settings.mirror_settings.mirrored_repository=Синхронизированное хранилище settings.mirror_settings.direction=Направление settings.mirror_settings.direction.pull=Отправка @@ -1683,13 +1714,13 @@ settings.site=Сайт settings.update_settings=Обновить настройки settings.branches.update_default_branch=Обновить ветку по умолчанию settings.advanced_settings=Расширенные настройки -settings.wiki_desc=Включить Вики для репозитория +settings.wiki_desc=Включить Описание для проекта settings.use_internal_wiki=Использовать встроенную вики-систему settings.use_external_wiki=Использовать внешнюю вики-систему -settings.external_wiki_url=URL-адрес внешней Вики -settings.external_wiki_url_error=URL внешней вики не является допустимым URL-адресом. +settings.external_wiki_url=URL-адрес внешнего Описания +settings.external_wiki_url_error=URL внешнего Описания не является допустимым URL-адресом. settings.external_wiki_url_desc=Посетители будут перенаправлены на URL-адрес, когда они кликнут по вкладке. -settings.issues_desc=Включить систему учёта задач репозитория +settings.issues_desc=Включить систему учёта задач проекта settings.use_internal_issue_tracker=Использовать встроенную систему учета задач settings.use_external_issue_tracker=Использовать внешнюю систему учета задач settings.external_tracker_url=URL внешней системы отслеживания ошибок @@ -1712,7 +1743,7 @@ settings.pulls.allow_squash_commits=Разрешить объединять ко settings.pulls.allow_manual_merge=Пометить PR как слитый вручную settings.pulls.enable_autodetect_manual_merge=Включить автоопределение ручного слияния (Примечание: в некоторых особых случаях могут возникнуть ошибки) settings.pulls.default_delete_branch_after_merge=Удалить ветку запроса после его слияния по умолчанию -settings.projects_desc=Включить проекты репозитория +settings.projects_desc=Включитьнаправления проекта settings.admin_settings=Настройки администратора settings.admin_enable_health_check=Выполнять проверки целостности этого репозитория (git fsck) settings.admin_code_indexer=Индексатор кода @@ -1740,16 +1771,16 @@ settings.transfer.success=Трансфер репозитория успешно settings.transfer_abort=Отменить трансфер settings.transfer_abort_invalid=Невозможно отменить трансфер несуществующего репозитория. settings.transfer_abort_success=Трансфер репозитория в %s успешно отменён. -settings.transfer_desc=Передать репозиторий другому пользователю или организации где у вас есть права администратора. +settings.transfer_desc=Передать проект другому пользователю или сообщества где у вас есть права администратора. settings.transfer_form_title=Введите сопутствующую информацию для подтверждения операции: settings.transfer_in_progress=Трансфер в процессе выполнения. Отмените его, если желаете выполнить трансфер другому пользователю. settings.transfer_notices_1=- Вы можете потерять доступ, если новый владелец является отдельным пользователем. -settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет организация, владельцем которой вы являетесь. -settings.transfer_notices_3=- если репозиторий является приватным и передается отдельному пользователю, это действие позволяет убедиться, что пользователь имеет хотя бы права на чтение (и при необходимости изменяет права доступа). +settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет сообщество, владельцем которой вы являетесь. +settings.transfer_notices_3=- если проект является приватным и передается отдельному пользователю, это действие позволяет убедиться, что пользователь имеет хотя бы права на чтение (и при необходимости изменяет права доступа). settings.transfer_owner=Новый владелец settings.transfer_perform=Выполнить трансфер settings.transfer_started=Репозиторий ожидает подтверждения трансфера от "%s" -settings.transfer_succeed=Репозиторий перенесён. +settings.transfer_succeed=Проект перенесён. settings.signing_settings=Настройки подписи верификации settings.trust_model=Модель доверия подписи settings.trust_model.default=Модель доверия по умолчанию @@ -1758,47 +1789,47 @@ settings.trust_model.collaborator=Соавтор settings.trust_model.collaborator.long=Соавтор: Подписи доверия от соавторов settings.trust_model.collaborator.desc=Допустимые подписи соавторов этого репозитория будут помечены как "доверенные" - (если они соответствуют коммиту или нет). В противном случае, правильные подписи будут помечены как "ненадёжные", если подпись соответствует коммиту и "не совпадает", если нет. settings.trust_model.committer=Коммитер -settings.trust_model.committer.long=Коммитер: Доверять подписям, соответствующим коммитерам (Это совпадает с GitHub и заставит подписать коммиты Gitea в качестве коммитера) -settings.trust_model.committer.desc=Допустимые подписи будут помечены "доверенными" только если они соответствуют коммитеру, в противном случае они будут помечены "недоверенными". Это заставит Gitea быть коммитером подписанных коммитов вместе с фактическим коммитером, обозначенным как Co-Authored-By: и Co-Committed-By: прикреплён в этом коммите. Ключ Gitea по умолчанию должен совпадать с пользователем в базе данных. +settings.trust_model.committer.long=Коммитер: Доверять подписям, соответствующим коммитерам (Это совпадает с GitHub и заставит подписать коммиты Мирокод в качестве коммитера) +settings.trust_model.committer.desc=Допустимые подписи будут помечены "доверенными" только если они соответствуют коммитеру, в противном случае они будут помечены "недоверенными". Это заставит Мирокод быть коммитером подписанных коммитов вместе с фактическим коммитером, обозначенным как Co-Authored-By: и Co-Committed-By: прикреплён в этом коммите. Ключ Мирокод по умолчанию должен совпадать с пользователем в базе данных. settings.trust_model.collaboratorcommitter=Соавтор+Коммитер settings.trust_model.collaboratorcommitter.long=Соавтор+Коммитер: Доверять подписи соавторам, которые соответствуют коммитеру -settings.trust_model.collaboratorcommitter.desc=Допустимые подписи соавторов этого репозитория будут помечены "доверенными", если они соответствуют коммиту. В противном случае, правильные подписи будут помечены как "недоверенными", если подпись соответствует коммиту и не совпадает. Это заставит Gitea быть отмеченным в качестве ответственного за подписание коммитеров с фактическим коммитером, обозначенным как Co-Authored-By: и Co-Committed-By: прикреплённым для выполнения этого коммита. По умолчанию ключ Gitea должен совпадать с пользователем в базе данных. -settings.wiki_delete=Стереть данные Вики -settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите Вики — пути назад не будет. -settings.wiki_delete_notices_1=- Это навсегда удалит и отключит Вики для %s. -settings.confirm_wiki_delete=Стереть данные Вики -settings.wiki_deletion_success=Данные Вики успешно стерты. -settings.delete=Удалить этот репозиторий -settings.delete_desc=Будьте внимательны! Как только вы удалите репозиторий — пути назад не будет. +settings.trust_model.collaboratorcommitter.desc=Допустимые подписи соавторов этого репозитория будут помечены "доверенными", если они соответствуют коммиту. В противном случае, правильные подписи будут помечены как "недоверенными", если подпись соответствует коммиту и не совпадает. Это заставит Мирокод быть отмеченным в качестве ответственного за подписание коммитеров с фактическим коммитером, обозначенным как Co-Authored-By: и Co-Committed-By: прикреплённым для выполнения этого коммита. По умолчанию ключ Мирокод должен совпадать с пользователем в базе данных. +settings.wiki_delete=Стереть описание проекта +settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите описание проекта — пути назад не будет. +settings.wiki_delete_notices_1=- Это навсегда удалит и отключит Описание проекта для %s. +settings.confirm_wiki_delete=Стереть Описание проекта +settings.wiki_deletion_success=Описание проекта успешно стерто. +settings.delete=Удалить этот проект +settings.delete_desc=Будьте внимательны! Как только вы удалите проект — пути назад не будет. settings.delete_notices_1=- Эта операция НЕ МОЖЕТ быть отменена. settings.delete_notices_2=- Эта операция навсегда удалит всё из репозитория %s, включая данные Git, связанные с ним задачи, комментарии и права доступа для сотрудников. settings.delete_notices_fork_1=- Все форки станут независимыми репозиториями после удаления. -settings.deletion_success=Репозиторий удалён. +settings.deletion_success=Проект удалён. settings.update_settings_success=Настройки репозитория обновлены. settings.confirm_delete=Удалить репозиторий settings.add_collaborator=Добавить соавтора settings.add_collaborator_success=Соавтор добавлен. settings.add_collaborator_inactive_user=Невозможно добавить неактивного пользователя как соавтора. -settings.add_collaborator_duplicate=Соавтор уже добавлен в этот репозиторий. +settings.add_collaborator_duplicate=Соавтор уже добавлен в этот проект. settings.delete_collaborator=Удалить settings.collaborator_deletion=Удалить соавтора -settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом репозитории после удаления. Вы хотите продолжить? +settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом проекте после удаления. Вы хотите продолжить? settings.remove_collaborator_success=Соавтор удалён. settings.search_user_placeholder=Поиск пользователя… -settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы. -settings.change_team_access_not_allowed=Доступ к репозиторию команде был ограничен владельцем организации -settings.team_not_in_organization=Команда не в той же организации, что и репозиторий +settings.org_not_allowed_to_be_collaborator=Сообщества не могут быть добавлены как соавторы. +settings.change_team_access_not_allowed=Доступ к проекту команде был ограничен владельцем сообщества +settings.team_not_in_organization=Команда не в той же сообщества, что и проект settings.teams=Команды settings.add_team=Добавить команду -settings.add_team_duplicate=Команда уже имеет репозиторий -settings.add_team_success=Команда теперь имеет доступ к репозиторию. +settings.add_team_duplicate=Команда уже имеет проект +settings.add_team_success=Команда теперь имеет доступ к проекту. settings.search_team=Поиск команды… -settings.change_team_permission_tip=Разрешение команды установлено на странице настройки команды и не может быть изменено для каждого репозитория -settings.delete_team_tip=Эта команда имеет доступ ко всем репозиториям и не может быть удалена -settings.remove_team_success=Доступ команды к репозиторию был удалён. +settings.change_team_permission_tip=Разрешение команды установлено на странице настройки команды и не может быть изменено для каждого проекта +settings.delete_team_tip=Эта команда имеет доступ ко всем проектам и не может быть удалена +settings.remove_team_success=Доступ команды к проекту был удалён. settings.add_webhook=Добавить Вебхук settings.add_webhook.invalid_channel_name=Название канала вебхука не может быть пустым или состоять только из символа #. -settings.hooks_desc=Вебхуки позволяют внешним службам получать уведомления при возникновении определенных событий на Gitea. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем руководстве по вебхукам. +settings.hooks_desc=Вебхуки позволяют внешним службам получать уведомления при возникновении определенных событий на Мирокод. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем руководстве по вебхукам. settings.webhook_deletion=Удалить вебхук settings.webhook_deletion_desc=Удаление этого веб-хука приведет к удалению всей связанной с ним информации, включая историю. Хотите продолжить? settings.webhook_deletion_success=Вебхук был удалён. @@ -1813,7 +1844,7 @@ settings.githook_edit_desc=Если хук не активен, будет по settings.githook_name=Название Hook'a settings.githook_content=Содержание hook'а settings.update_githook=Обновить Hook -settings.add_webhook_desc=Gitea будет оправлять POST запросы на указанный URL адрес, с информацией о происходящих событиях. Подробности на странице инструкции по использованию вебхуков. +settings.add_webhook_desc=Мирокод будет оправлять POST запросы на указанный URL адрес, с информацией о происходящих событиях. Подробности на странице инструкции по использованию вебхуков. settings.payload_url=URL обработчика settings.http_method=Метод HTTP settings.content_type=Тип содержимого @@ -1827,19 +1858,19 @@ settings.event_desc=На какие события этот веб-хук дол settings.event_push_only=Просто push событие settings.event_send_everything=Все события settings.event_choose=Позвольте мне выбрать то, что нужно. -settings.event_header_repository=События репозитория +settings.event_header_repository=События проекта settings.event_create=Создать settings.event_create_desc=Ветка или тэг созданы. settings.event_delete=Удалить settings.event_delete_desc=Ветка или тег удалены. settings.event_fork=Форкнуть -settings.event_fork_desc=Репозиторий форкнут. +settings.event_fork_desc=Проект форкнут. settings.event_release=Релиз -settings.event_release_desc=Релиз опубликован, обновлён или удалён из репозитория. +settings.event_release_desc=Релиз опубликован, обновлён или удалён из проекта. settings.event_push=Push -settings.event_push_desc=Push в репозиторий. -settings.event_repository=Репозиторий -settings.event_repository_desc=Репозиторий создан или удален. +settings.event_push_desc=Push в проект. +settings.event_repository=Проект +settings.event_repository_desc=Проект создан или удален. settings.event_header_issue=События задачи settings.event_issues=Задачи settings.event_issues_desc=Задача открыта, закрыта, переоткрыта или отредактирована. @@ -1876,22 +1907,22 @@ settings.update_hook_success=Вебхук был обновлён. settings.delete_webhook=Удалить вебхук settings.recent_deliveries=Недавние рассылки settings.hook_type=Тип hook'а -settings.add_slack_hook_desc=Добавить интеграцию с Slack в ваш репозиторий. +settings.add_slack_hook_desc=Добавить интеграцию с Slack в ваш проект. settings.slack_token=Slack токен settings.slack_domain=Домен settings.slack_channel=Канал settings.add_discord_hook_desc=Добавить уведомления о событиях через Discord. -settings.add_dingtalk_hook_desc=Добавить интеграцию с Dingtalk в ваш репозиторий. -settings.add_telegram_hook_desc=Добавить интеграцию с Telegram в ваш репозиторий. -settings.add_matrix_hook_desc=Добавить интеграцию Matrix в ваш репозиторий. -settings.add_msteams_hook_desc=Добавить интеграцию с Microsoft Teams в ваш репозиторий. -settings.add_feishu_hook_desc=Добавить интеграцию Feishu в ваш репозиторий. -settings.add_Wechat_hook_desc=Добавить интеграцию с Wechatwork в ваш репозиторий. +settings.add_dingtalk_hook_desc=Добавить интеграцию с Dingtalk в ваш проект. +settings.add_telegram_hook_desc=Добавить интеграцию с Telegram в ваш проект. +settings.add_matrix_hook_desc=Добавить интеграцию Matrix в ваш проект. +settings.add_msteams_hook_desc=Добавить интеграцию с Microsoft Teams в ваш проект. +settings.add_feishu_hook_desc=Добавить интеграцию Feishu в ваш проект. +settings.add_Wechat_hook_desc=Добавить интеграцию с Wechatwork в ваш проект. settings.deploy_keys=Ключи развертывания settings.add_deploy_key=Добавить ключ развертывания settings.deploy_key_desc=Ключи развёртывания доступны только для чтения. Это не то же самое что и SSH-ключи аккаунта. settings.is_writable=Разрешить запись -settings.is_writable_info=Может ли этот ключ быть использован для выполнения push в репозиторий? Ключи развёртывания всегда имеют доступ на pull. +settings.is_writable_info=Может ли этот ключ быть использован для выполнения push в проект? Ключи развёртывания всегда имеют доступ на pull. settings.no_deploy_keys=Вы не добавляли ключи развертывания. settings.title=Заголовок settings.deploy_key_content=Содержимое @@ -1926,7 +1957,7 @@ settings.protect_merge_whitelist_users=Пользователи с правом settings.protect_merge_whitelist_teams=Команды, члены которых обладают правом на слияние: settings.protect_check_status_contexts=Включить проверку статуса settings.protect_check_status_contexts_desc=Требуется пройти проверку состояния перед слиянием. Выберите, какие проверки состояния должны быть пройдены, прежде чем ветви можно будет объединить в ветвь, соответствующую этому правилу. Если этот параметр включен, коммиты сначала должны быть перемещены в другую ветвь, а затем объединены или перемещены непосредственно в ветвь, соответствующую этому правилу, после прохождения проверки состояния. Если контексты не выбраны, то последняя фиксация должна быть успешной независимо от контекста. -settings.protect_check_status_contexts_list=Проверки состояния за последнюю неделю для этого репозитория +settings.protect_check_status_contexts_list=Проверки состояния за последнюю неделю для этого проекта settings.protect_required_approvals=Необходимые одобрения: settings.protect_required_approvals_desc=Разрешить принятие запроса на слияние только с достаточным количеством положительных отзывов. settings.protect_approvals_whitelist_enabled=Ограничить утверждения белым списком пользователей или команд @@ -1988,10 +2019,10 @@ settings.unarchive.header=Разархивировать этот репозит settings.unarchive.text=Разархивация восстанавливает возможность совершать push в репозиторий, создавать новые коммиты, задачи и запросы на слияние. settings.unarchive.success=Репозиторий был успешно разархивирован. settings.unarchive.error=Ошибка при попытке разархивировать репозиторий. Смотрите логи для получения подробностей. -settings.update_avatar_success=Аватар репозитория обновлён. +settings.update_avatar_success=Аватар проекта обновлён. settings.lfs=LFS settings.lfs_filelist=Файлы LFS хранятся в этом репозитории -settings.lfs_no_lfs_files=Нет файлов LFS в этом репозитории +settings.lfs_no_lfs_files=Нет файлов Large file storage (LFS) в этом репозитории1 settings.lfs_findcommits=Найти коммиты settings.lfs_lfs_file_no_commits=Для этого LFS файла не найдено коммитов settings.lfs_noattribute=Этот путь не имеет блокируемого атрибута в ветке по умолчанию @@ -2010,7 +2041,7 @@ settings.lfs_force_unlock=Принудительная разблокировк settings.lfs_pointers.found=Найдено %d указатель(ей) блоков - присоединено %d, %d не привязано (%d отсутствует в хранилище) settings.lfs_pointers.sha=Blob SHA settings.lfs_pointers.oid=OID -settings.lfs_pointers.inRepo=В репозитории +settings.lfs_pointers.inRepo=В проекте settings.lfs_pointers.exists=Существуют в хранилище settings.lfs_pointers.accessible=Доступно для пользователя settings.lfs_pointers.associateAccessible=Связать доступные %d OID @@ -2158,15 +2189,15 @@ error.csv.unexpected=Не удается отобразить этот файл, error.csv.invalid_field_count=Не удается отобразить этот файл, потому что он имеет неправильное количество полей в строке %d. [org] -org_name_holder=Название организации -org_full_name_holder=Полное название организации -org_name_helper=Лучшие названия организаций коротки и запоминаемы. -create_org=Создать организацию +org_name_holder=Название сообщества +org_full_name_holder=Полное название сообщества +org_name_helper=Лучшие названия сообществ коротки и запоминаемы. +create_org=Создать сообщество repo_updated=Обновлено people=Люди teams=Команды lower_members=Участники -lower_repositories=Репозитории +lower_repositories=Проекты create_new_team=Создание команды create_team=Создать команду org_desc=Описание @@ -2174,43 +2205,44 @@ team_name=Название команды team_desc=Описание team_name_helper=Названия команд должны быть короткими и запоминающимися. team_desc_helper=Что это за команда? -team_access_desc=Доступ к репозиторию +team_access_desc=Доступ к проекту team_permission_desc=Разрешение -team_unit_desc=Разрешить доступ к разделам репозитория +team_unit_desc=Разрешить доступ к разделам проекта team_unit_disabled=(Отключено) -form.name_reserved=Наименование организации '%s' зарезервированно. -form.name_pattern_not_allowed=Шаблон организации '%s' не допускается. -form.create_org_not_allowed=Этому пользователю не разрешено создавать организацию. +form.name_reserved=Наименование сообщества '%s' зарезервированно. +form.name_pattern_not_allowed=Шаблон сообщества '%s' не допускается. +form.create_org_not_allowed=Этому пользователю не разрешено создавать сообщество. settings=Настройки -settings.options=Организация +settings.options=Сообщество settings.full_name=Полное имя settings.website=Сайт settings.location=Местоположение +settings.locationcoordinate=Координаты settings.permission=Разрешения -settings.repoadminchangeteam=Администратор репозитория может добавлять и удалять права доступа для команд +settings.repoadminchangeteam=Администратор проекта может добавлять и удалять права доступа для команд settings.visibility=Видимость settings.visibility.public=Публичный settings.visibility.limited=Ограничено (Видно только для авторизованных пользователей) settings.visibility.limited_shortname=Ограничить -settings.visibility.private=Частный (Видимый только для участников организации) +settings.visibility.private=Частный (Видимый только для участников сообщества) settings.visibility.private_shortname=Приватный settings.update_settings=Обновить настройки -settings.update_setting_success=Настройки организации обновлены. -settings.change_orgname_prompt=Это изменение изменит ссылки на организацию. +settings.update_setting_success=Настройки сообщества обновлены. +settings.change_orgname_prompt=Это изменение изменит ссылки на сообщество. settings.change_orgname_redirect_prompt=Старое имя будет перенаправлено до тех пор, пока оно не будет введено. -settings.update_avatar_success=Аватар организации обновлён. -settings.delete=Удалить организацию -settings.delete_account=Удалить эту организацию -settings.delete_prompt=Это действие БЕЗВОЗВРАТНО удалит эту организацию навсегда. +settings.update_avatar_success=Аватар сообщества обновлён. +settings.delete=Удалить сообщество +settings.delete_account=Удалить это сообщество +settings.delete_prompt=Это действие БЕЗВОЗВРАТНО удалит это сообщество навсегда. settings.confirm_delete_account=Подтвердить удаление -settings.delete_org_title=Удалить организацию -settings.delete_org_desc=Эта организация будет безвозвратно удалена. Продолжить? -settings.hooks_desc=Добавьте вебхуки, которые будет вызываться для всех репозиториев под этой организации. +settings.delete_org_title=Удалить сообщество +settings.delete_org_desc=Это сообщество будет безвозвратно удалена. Продолжить? +settings.hooks_desc=Добавьте вебхуки, которые будет вызываться для всех репозиториев под этим сообществом. -settings.labels_desc=Добавьте метки, которые могут быть использованы в задачах для всех репозиториев этой организации. +settings.labels_desc=Добавьте метки, которые могут быть использованы в задачах для всех репозиториев этого сообщества. members.membership_visibility=Видимость участника команды: members.public=Видимый @@ -2230,12 +2262,12 @@ members.invite_now=Пригласите сейчас teams.join=Объединить teams.leave=Выйти teams.leave.detail=Покинуть %s? -teams.can_create_org_repo=Создать репозитории -teams.can_create_org_repo_helper=Участники могут создавать новые репозитории в организации. Создатель получит администраторский доступ к новому репозиторию. -teams.read_access_helper=Участники могут просматривать и клонировать командные репозитории. -teams.write_access_helper=Участники могут читать и выполнять push в командные репозитории. +teams.can_create_org_repo=Создать проекты +teams.can_create_org_repo_helper=Участники могут создавать новые проекты в сообщество. Создатель получит администраторский доступ к новому репозиторию. +teams.read_access_helper=Участники могут просматривать и клонировать командные проекты. +teams.write_access_helper=Участники могут читать и выполнять push в командные проекты. teams.admin_access=Доступ администратора -teams.admin_access_helper=Участники могут выполнять pull, push в командные репозитории и добавлять соавторов в команду. +teams.admin_access_helper=Участники могут выполнять pull, push в командные проекты и добавлять соавторов в команду. teams.no_desc=Эта группа не имеет описания teams.settings=Настройки teams.owners_permission_desc=Владельцы имеют полный доступ ко всем репозиториям и имеют права администратора организации. @@ -2246,33 +2278,33 @@ teams.add_team_member=Добавление члена группы разраб teams.delete_team_title=Удалить команду teams.delete_team_desc=Удаление команды отменяет доступ к репозиторию для её членов. Продолжить? teams.delete_team_success=Команда удалена. -teams.read_permission_desc=Эта команда предоставляет доступ на Чтение: члены могут просматривать и клонировать репозитории команды. -teams.write_permission_desc=Эта команда предоставляет доступ на Запись: члены могут получать и выполнять push команды в репозитории. +teams.read_permission_desc=Эта команда предоставляет доступ на Чтение: члены могут просматривать и клонировать проекты команды. +teams.write_permission_desc=Эта команда предоставляет доступ на Запись: члены могут получать и выполнять push команды в проекты. teams.admin_permission_desc=Эта команда дает административный доступ: участники могут читать, пушить и добавлять соавторов к ее репозиториям. -teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право Создание репозитория: члены команды могут создавать новые репозитории в организации. -teams.repositories=Репозитории группы разработки -teams.search_repo_placeholder=Поиск репозитория… -teams.remove_all_repos_title=Удалить все репозитории команды -teams.remove_all_repos_desc=Удаляет все репозитории из команды. -teams.add_all_repos_title=Добавить все репозитории -teams.add_all_repos_desc=Добавит все репозитории организации в команду. +teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право Создание проекта: члены команды могут создавать новые репозитории в сообщество. +teams.repositories=Проекты группы разработки +teams.search_repo_placeholder=Поиск проекта… +teams.remove_all_repos_title=Удалить все проекты команды +teams.remove_all_repos_desc=Удаляет все проекты из команды. +teams.add_all_repos_title=Добавить все проекты +teams.add_all_repos_desc=Добавит все проекты сообщества в команду. teams.add_nonexistent_repo=Вы добавляете в отсутствующий репозиторий, пожалуйста сначала его создайте. teams.add_duplicate_users=Пользователь уже состоит в команде. teams.repos.none=Для этой команды нет доступных репозиториев. teams.members.none=В этой команде нет участников. -teams.specific_repositories=Конкретные репозитории -teams.specific_repositories_helper=Участники будут иметь доступ только к репозиториям, явно добавленным в команду. Выбор этого не автоматически удалит репозитории, уже добавленные в Все репозитории. -teams.all_repositories=Все репозитории -teams.all_repositories_helper=Команда имеет доступ ко всем репозиториям. Выбрав его, добавит все существующие репозитории в команду. -teams.all_repositories_read_permission_desc=Эта команда предоставляет прочтено доступ к всем репозиториям: участники могут просматривать и клонировать репозитории. -teams.all_repositories_write_permission_desc=Эта команда предоставляет Написать доступ к всем репозиториям: участники могут читать и выполнять push в репозитории. -teams.all_repositories_admin_permission_desc=Эта команда предоставляет администратору доступ к всем репозиториям: участники могут читать, отправлять сообщения и добавлять соавторов в репозитории. +teams.specific_repositories=Конкретные проекты +teams.specific_repositories_helper=Участники будут иметь доступ только к проектам, явно добавленным в команду. Выбор этого не автоматически удалит репозитории, уже добавленные в Все репозитории. +teams.all_repositories=Все проекты +teams.all_repositories_helper=Команда имеет доступ ко всем проектам. Выбрав его, добавит все существующие проекты в команду. +teams.all_repositories_read_permission_desc=Эта команда предоставляет прочтено доступ к всем проектам: участники могут просматривать и клонировать репозитории. +teams.all_repositories_write_permission_desc=Эта команда предоставляет Написать доступ к всем проектам: участники могут читать и выполнять push в репозитории. +teams.all_repositories_admin_permission_desc=Эта команда предоставляет администратору доступ к всем проектам: участники могут читать, отправлять сообщения и добавлять соавторов в репозитории. [admin] dashboard=Панель users=Пользователи -organizations=Организации -repositories=Репозитории +organizations=Сообщества +repositories=Проекты hooks=Веб-хуки authentication=Авторизация emails=Адреса эл. почты пользователей @@ -2286,7 +2318,7 @@ total=Всего: %d dashboard.statistic=Статистика dashboard.operations=Операции dashboard.system_status=Статус системного монитора -dashboard.statistic_info=В базе данных Gitea записано %d пользователей, %d организаций, %d публичных ключей, %d репозиториев, %d подписок на репозитории, %d добавлений в избранное, %d действий, %d доступов, %d задач, %d комментариев, %d социальных учетных записей, %d подписок на пользователей, %d зеркал, %d релизов, %d источников входа, %d вебхуков, %d этапов, %d меток, %d задач hook'ов, %d команд, %d задач по обновлению, %d присоединённых файлов. +dashboard.statistic_info=В базе данных Мирокод записано %d пользователей, %d сообществ, %d публичных ключей, %d репозиториев, %d подписок на репозитории, %d добавлений в избранное, %d действий, %d доступов, %d задач, %d комментариев, %d социальных учетных записей, %d подписок на пользователей, %d зеркал, %d релизов, %d источников входа, %d вебхуков, %d этапов, %d меток, %d задач hook'ов, %d команд, %d задач по обновлению, %d присоединённых файлов. dashboard.operation_name=Имя операции dashboard.operation_switch=Переключить dashboard.operation_run=Запуск @@ -2317,9 +2349,9 @@ dashboard.archive_cleanup=Удалить старые архивы репози dashboard.deleted_branches_cleanup=Очистка удалённых ветвей dashboard.update_migration_poster_id=Обновить ID плакатов миграции dashboard.git_gc_repos=Выполнить сборку мусора для всех репозиториев -dashboard.resync_all_sshkeys=Обновить файл '.ssh/authorized_keys' с SSH ключами Gitea. +dashboard.resync_all_sshkeys=Обновить файл '.ssh/authorized_keys' с SSH ключами Мирокод. dashboard.resync_all_sshkeys.desc=(Не требуется для встроенного SSH сервера.) -dashboard.resync_all_sshprincipals=Обновите файл '.ssh/authorized_principals' SSH данными участника Gitea. +dashboard.resync_all_sshprincipals=Обновите файл '.ssh/authorized_principals' SSH данными участника Мирокод. dashboard.resync_all_sshprincipals.desc=(Не требуется для встроенного SSH сервера.) dashboard.resync_all_hooks=Повторная синхронизация hook'ов pre-receive, update и post-receive во всех репозиториях. dashboard.reinit_missing_repos=Переинициализировать все отсутствующие Git репозитории, для которых существуют записи @@ -2365,7 +2397,7 @@ users.activated=Активирован users.admin=Администратор users.restricted=Ограничено users.2fa=Двухфакторная авторизация -users.repos=Репозитории +users.repos=Проекты users.created=Создано users.last_login=Последний вход users.never_login=Никогда не входил @@ -2378,20 +2410,20 @@ users.auth_login_name=Логин для авторизации users.password_helper=Оставьте пустым, чтобы оставить без изменений. users.update_profile_success=Профиль учётной записи обновлён успешно. users.edit_account=Изменение учетной записи -users.max_repo_creation=Максимальное количество репозиториев +users.max_repo_creation=Максимальное количество проектов users.max_repo_creation_desc=(Установите -1 для использования стандартного глобального значения предела) users.is_activated=Эта учетная запись активирована users.prohibit_login=Этой учетной записи запрещён вход в систему users.is_admin=У этой учетной записи есть права администратора users.is_restricted=Ограничен users.allow_git_hook=Эта учётная запись имеет разрешение на создание Git hook'ов -users.allow_git_hook_tooltip=Git Hooks выполняется как пользователь ОС с Gitea и будет иметь одинаковый уровень доступа к хосту. В результате пользователи с привилегией Git Hook могут получить доступ и модифицировать все репозитории Gitea, а также базу данных, используемую Gitea. Следовательно, они также могут получить привилегии администратора Gitea. -users.allow_import_local=Пользователь имеет право импортировать локальные репозитории -users.allow_create_organization=Эта учетная запись имеет разрешения на создание организаций +users.allow_git_hook_tooltip=Git Hooks выполняется как пользователь ОС с Мирокод и будет иметь одинаковый уровень доступа к хосту. В результате пользователи с привилегией Git Hook могут получить доступ и модифицировать все репозитории Мирокод, а также базу данных, используемую Мирокод. Следовательно, они также могут получить привилегии администратора Мирокод. +users.allow_import_local=Пользователь имеет право импортировать локальные проекты +users.allow_create_organization=Эта учетная запись имеет разрешения на создание сообществ users.update_profile=Обновить профиль учетной записи users.delete_account=Удалить эту учетную запись users.still_own_repo=На вашем аккаунте все еще остается один или более репозиториев, сначала вам нужно удалить или передать их. -users.still_has_org=Эта учетная запись все еще является членом одной или более организаций. Для продолжения, покиньте или удалите организации. +users.still_has_org=Эта учетная запись все еще является членом одной или более сообществ. Для продолжения, покиньте или удалите сообщества. users.deletion_success=Учётная запись успешно удалена. users.reset_2fa=Сброс 2FA users.list_status_filter.menu_text=Фильтр @@ -2420,15 +2452,15 @@ emails.duplicate_active=Этот адрес электронной почты у emails.change_email_header=Обновить свойства электронной почты emails.change_email_text=Вы уверены, что хотите обновить этот адрес электронной почты? -orgs.org_manage_panel=Управление организациями +orgs.org_manage_panel=Управление сообществами orgs.name=Название orgs.teams=Команды orgs.members=Участники -orgs.new_orga=Новая организация +orgs.new_orga=Новое сообщество -repos.repo_manage_panel=Управление репозиториями -repos.unadopted=Непринятые репозитории -repos.unadopted.no_more=Больше непринятых репозиториев не найдено +repos.repo_manage_panel=Управление проектами +repos.unadopted=Непринятые проекты +repos.unadopted.no_more=Больше непринятых проектов не найдено repos.owner=Владелец repos.name=Название repos.private=Личный @@ -2439,12 +2471,12 @@ repos.issues=Задачи repos.size=Размер defaulthooks=Стандартные Веб-хуки -defaulthooks.desc=Вебхуки автоматически делают HTTP-POST запросы на сервер, когда вызываются определенные события Gitea. Вебхуки, определённые здесь, по умолчанию и будут скопированы во все новые репозитории. Подробнее читайте в руководстве по вебхукам. +defaulthooks.desc=Вебхуки автоматически делают HTTP-POST запросы на сервер, когда вызываются определенные события Мирокод. Вебхуки, определённые здесь, по умолчанию и будут скопированы во все новые репозитории. Подробнее читайте в руководстве по вебхукам. defaulthooks.add_webhook=Добавить стандартный Веб-хук defaulthooks.update_webhook=Обновить стандартный Веб-хук systemhooks=Системные вебхуки -systemhooks.desc=Вебхуки автоматически делают HTTP-POST запросы на сервер, когда вызываются определённые события Gitea. Определённые вебхуки будут действовать на всех репозиториях системы, поэтому пожалуйста, учитывайте любые последствия для производительности. Подробнее читайте в руководстве по вебхукам. +systemhooks.desc=Вебхуки автоматически делают HTTP-POST запросы на сервер, когда вызываются определённые события Мирокод. Определённые вебхуки будут действовать на всех репозиториях системы, поэтому пожалуйста, учитывайте любые последствия для производительности. Подробнее читайте в руководстве по вебхукам. systemhooks.add_webhook=Добавить системный вебхук systemhooks.update_webhook=Обновить системный вебхук @@ -2555,8 +2587,8 @@ auths.login_source_of_type_exist=Источник аутентификации config.server_config=Конфигурация сервера config.app_name=Название сайта -config.app_ver=Версия Gitea -config.app_url=Базовый URL-адрес Gitea +config.app_ver=Версия Мирокод +config.app_url=Базовый URL-адрес Мирокод config.custom_conf=Путь к файлу конфигурации config.custom_file_root_path=Пользовательский путь до папки с файлами config.domain=Домен сервера @@ -2565,7 +2597,7 @@ config.disable_router_log=Отключение журнала маршрутиз config.run_user=Запуск от имени пользователя config.run_mode=Режим выполнения config.git_version=Версия Git -config.repo_root_path=Путь до папки репозиториев +config.repo_root_path=Путь до папки проектов config.lfs_root_path=Корневой путь LFS config.static_file_root_path=Путь до папки со статичными файлами config.log_file_root_path=Путь к журналу @@ -2601,7 +2633,7 @@ config.db_path=Путь config.service_config=Сервисная конфигурация config.register_email_confirm=Требуется подтверждение по электронной почте config.disable_register=Отключить самостоятельную регистрацию -config.allow_only_internal_registration=Разрешить регистрацию только через Gitea +config.allow_only_internal_registration=Разрешить регистрацию только через Мирокод config.allow_only_external_registration=Разрешить регистрацию только через сторонние сервисы config.enable_openid_signup=Включить cамостоятельную регистрацию OpenID config.enable_openid_signin=Включение входа через OpenID @@ -2613,13 +2645,14 @@ config.enable_captcha=Включить CAPTCHA config.active_code_lives=Время жизни кода для активации config.reset_password_code_lives=Время действия кода восстановления аккаунта config.default_keep_email_private=Скрывать адреса электронной почты по умолчанию -config.default_allow_create_organization=Разрешить создание организаций по умолчанию +config.default_allow_create_organization=Разрешить создание сообществ по умолчанию config.enable_timetracking=Включить отслеживание времени config.default_enable_timetracking=Включить отслеживание времени по умолчанию config.default_allow_only_contributors_to_track_time=Учитывать только участников разработки в подсчёте времени config.no_reply_address=No-reply адрес -config.default_visibility_organization=Видимость по умолчанию для новых организаций +config.default_visibility_organization=Видимость по умолчанию для новых сообществ config.default_enable_dependencies=Включение зависимостей для задач по умолчанию +config.default_enable_parents=Включение родителей для задач по умолчанию config.webhook_config=Конфигурация вебхуков config.queue_length=Длина очереди @@ -2764,15 +2797,15 @@ notices.inverse_selection=Инверсия выделения notices.delete_selected=Удалить выбранные notices.delete_all=Удалить все уведомления notices.type=Тип -notices.type_1=Репозиторий +notices.type_1=Проект notices.type_2=Задача notices.desc=Описание notices.op=Oп. notices.delete_success=Уведомления системы были удалены. [action] -create_repo=создал(а) репозиторий %s -rename_repo=переименовал(а) репозиторий из %[1]s на %[3]s +create_repo=создал(а) Проект %s +rename_repo=переименовал(а) Проект из %[1]s на %[3]s commit_repo=отправил(а) изменения в %[3]s в %[4]s create_issue=`открыл(а) задачу %[3]s#%[2]s` close_issue=`закрыл(а) задачу %[3]s#%[2]s` @@ -2783,7 +2816,7 @@ reopen_pull_request=`переоткрыл(а) запрос на слияние < comment_issue=`прокомментировал(а) задачу %[3]s#%[2]s` comment_pull=`прокомментировал(а) запрос на слияние %[3]s#%[2]s` merge_pull_request=`принял(а) запрос на слияние %[3]s#%[2]s` -transfer_repo=передал(а) репозиторий %s %s +transfer_repo=передал(а) Проект %s %s push_tag=создал(а) тег %[3]s в %[4]s delete_tag=удалил(а) тэг %[2]s из %[3]s delete_branch=удалил(а) ветку %[2]s из %[3]s diff --git a/package-lock.json b/package-lock.json index 2f8d921e21..e7e1a78627 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1711,6 +1711,8 @@ "version": "8.9.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -1725,7 +1727,9 @@ "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "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": { "version": "3.5.2", @@ -2119,13 +2123,19 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001301", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", - "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -11189,14 +11199,13 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - }, + "requires": {}, "dependencies": { "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "version": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "optional": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -11207,7 +11216,9 @@ "json-schema-traverse": { "version": "1.0.0", "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": { - "version": "1.0.30001301", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz", - "integrity": "sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==" + "version": "1.0.30001374", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", + "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==" }, "chalk": { "version": "4.1.2", diff --git a/public/img/logo.png b/public/img/logo.png index c7971f9183..35d3130230 100644 Binary files a/public/img/logo.png and b/public/img/logo.png differ diff --git a/public/img/logo.svg b/public/img/logo.svg index afeeacb77c..e3db89903e 100644 --- a/public/img/logo.svg +++ b/public/img/logo.svg @@ -1 +1,107 @@ - \ No newline at end of file + +image/svg+xml diff --git a/public/img/logo1.svg b/public/img/logo1.svg new file mode 100644 index 0000000000..65fe4bc970 --- /dev/null +++ b/public/img/logo1.svg @@ -0,0 +1,102 @@ + +image/svg+xml diff --git a/public/img/logo2.svg b/public/img/logo2.svg new file mode 100644 index 0000000000..fe2aade512 --- /dev/null +++ b/public/img/logo2.svg @@ -0,0 +1,51 @@ + +image/svg+xml \ No newline at end of file diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index bdfe87fd4e..40d67f5e62 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -62,6 +62,7 @@ func CreateOrg(ctx *context.APIContext) { Description: form.Description, Website: form.Website, Location: form.Location, + LocationCoordinate: form.LocationCoordinate, IsActive: true, Type: user_model.UserTypeOrganization, Visibility: visibility, diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 7262073bbd..a987e59096 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -240,6 +240,9 @@ func EditUser(ctx *context.APIContext) { if form.Location != nil { u.Location = *form.Location } + if form.LocationCoordinate != nil { + u.LocationCoordinate = *form.LocationCoordinate + } if form.Description != nil { u.Description = *form.Description } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 133cce3416..817d6d5c98 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -267,6 +267,7 @@ func Create(ctx *context.APIContext) { Description: form.Description, Website: form.Website, Location: form.Location, + LocationCoordinate: form.LocationCoordinate, IsActive: true, Type: user_model.UserTypeOrganization, Visibility: visibility, @@ -340,6 +341,7 @@ func Edit(ctx *context.APIContext) { org.Description = form.Description org.Website = form.Website org.Location = form.Location + org.LocationCoordinate = form.LocationCoordinate if form.Visibility != "" { org.Visibility = api.VisibilityModes[form.Visibility] } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 851505fe4a..7449ecabcf 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -776,6 +776,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { EnableTimetracker: opts.InternalTracker.EnableTimeTracker, AllowOnlyContributorsToTrackTime: opts.InternalTracker.AllowOnlyContributorsToTrackTime, EnableDependencies: opts.InternalTracker.EnableIssueDependencies, + EnableParents: opts.InternalTracker.EnableIssueParents, } } 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 @@ -783,6 +784,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { EnableTimetracker: true, AllowOnlyContributorsToTrackTime: true, EnableDependencies: true, + EnableParents: true, } } else { config = unit.IssuesConfig() diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go index 5f4d76ed72..8f01da4543 100644 --- a/routers/api/v1/user/settings.go +++ b/routers/api/v1/user/settings.go @@ -57,6 +57,9 @@ func UpdateUserSettings(ctx *context.APIContext) { if form.Location != nil { ctx.User.Location = *form.Location } + if form.LocationCoordinate != nil { + ctx.User.LocationCoordinate = *form.LocationCoordinate + } if form.Language != nil { ctx.User.Language = *form.Language } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 1576f58b41..4b94fe9e10 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -371,7 +371,9 @@ func EditUserPost(ctx *context.Context) { emailChanged := !strings.EqualFold(u.Email, form.Email) u.Email = form.Email u.Website = form.Website + u.Description = form.Description u.Location = form.Location + u.LocationCoordinate = form.LocationCoordinate u.MaxRepoCreation = form.MaxRepoCreation u.IsActive = form.Active u.IsAdmin = form.Admin diff --git a/routers/web/map/umap.go b/routers/web/map/umap.go new file mode 100644 index 0000000000..e53e84ae13 --- /dev/null +++ b/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) +} diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 081e103f79..16f83f8b80 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -100,6 +100,7 @@ func SettingsPost(ctx *context.Context) { org.Description = form.Description org.Website = form.Website org.Location = form.Location + org.LocationCoordinate = form.LocationCoordinate org.RepoAdminChangeTeamAccess = form.RepoAdminChangeTeamAccess visibilityChanged := form.Visibility != org.Visibility diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 248743471b..8d1f4986d6 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -47,6 +47,7 @@ const ( tplAttachment base.TplName = "repo/issue/view_content/attachments" tplIssues base.TplName = "repo/issue/list" + tplIssuesTree base.TplName = "repo/issue/tree" tplIssueNew base.TplName = "repo/issue/new" tplIssueChoose base.TplName = "repo/issue/choose" 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 viewType := ctx.FormString("type") sortType := ctx.FormString("sort") @@ -210,7 +211,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } else { total = int(issueStats.ClosedCount) } - pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) + pager := context.NewPagination(total, page_size, page, 5) var mileIDs []int64 if milestoneID > 0 { @@ -224,7 +225,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti issues, err = models.Issues(&models.IssuesOptions{ ListOptions: db.ListOptions{ Page: pager.Paginater.Current(), - PageSize: setting.UI.IssuePagingNum, + PageSize: page_size, }, RepoID: repo.ID, AssigneeID: assigneeID, @@ -391,7 +392,7 @@ func Issues(ctx *context.Context) { 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() { return } @@ -412,6 +413,47 @@ func Issues(ctx *context.Context) { 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 func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.Repository) { 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 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 } @@ -1324,6 +1369,12 @@ func ViewIssue(ctx *context.Context) { // check if dependencies can be created across repositories 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 { ctx.ServerError("roleDescriptor", err) return @@ -1422,6 +1473,13 @@ func ViewIssue(ctx *context.Context) { 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 { comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ URLPrefix: ctx.Repo.RepoLink, @@ -1650,6 +1708,18 @@ func ViewIssue(ctx *context.Context) { 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["NumParticipants"] = len(participants) ctx.Data["Issue"] = issue diff --git a/routers/web/repo/issue_parent.go b/routers/web/repo/issue_parent.go new file mode 100644 index 0000000000..364c06efce --- /dev/null +++ b/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) +} diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index df5fd411b4..e8660392d8 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -289,7 +289,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) { ctx.Data["Title"] = milestone.Name 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["CanWriteIssues"] = ctx.Repo.CanWriteIssuesOrPulls(false) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index f89bffb00f..f0548aaffe 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -434,6 +434,7 @@ func SettingsPost(ctx *context.Context) { EnableTimetracker: form.EnableTimetracker, AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, EnableDependencies: form.EnableIssueDependencies, + EnableParents: form.EnableIssueParents, }, }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e98d94f7ca..da328ded45 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -697,6 +697,16 @@ func Home(ctx *context.Context) { return } + Wiki(ctx) +} + +// Code render repository page +func Code(ctx *context.Context) { + checkHomeCodeViewable(ctx) + if ctx.Written() { + return + } + renderCode(ctx) } diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index ab156b43fc..ddc5a7c47d 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -119,6 +119,7 @@ func ProfilePost(ctx *context.Context) { ctx.User.KeepEmailPrivate = form.KeepEmailPrivate ctx.User.Website = form.Website ctx.User.Location = form.Location + ctx.User.LocationCoordinate = form.LocationCoordinate ctx.User.Description = form.Description ctx.User.KeepActivityPrivate = form.KeepActivityPrivate ctx.User.Visibility = form.Visibility diff --git a/routers/web/web.go b/routers/web/web.go index e18ed8f8ee..aa946dd343 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -28,6 +28,7 @@ import ( "code.gitea.io/gitea/routers/web/dev" "code.gitea.io/gitea/routers/web/events" "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/repo" "code.gitea.io/gitea/routers/web/user" @@ -254,6 +255,9 @@ func RegisterRoutes(m *web.Route) { m.Get("/organizations", explore.Organizations) m.Get("/code", explore.Code) }, ignExploreSignIn) + m.Group("/map", func() { + m.Get("/umap", umap.UsersMap) + }, ignExploreSignIn) m.Get("/issues", reqSignIn, user.Issues) m.Get("/pulls", reqSignIn, user.Pulls) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) @@ -737,6 +741,10 @@ func RegisterRoutes(m *web.Route) { m.Post("/add", repo.AddDependency) 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.Group("/times", func() { m.Post("/add", bindIgnErr(forms.AddTimeManuallyForm{}), repo.AddTimeManually) @@ -877,6 +885,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/{username}/{reponame}", func() { m.Group("", func() { m.Get("/{type:issues|pulls}", repo.Issues) + m.Get("/issues_tree", repo.IssuesTree) m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue) m.Group("/{type:issues|pulls}/{index}/content-history", func() { m.Get("/overview", repo.GetContentHistoryOverview) @@ -1033,11 +1042,11 @@ func RegisterRoutes(m *web.Route) { }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) m.Group("/src", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Code) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Code) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Code) // "/*" 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) m.Group("", func() { @@ -1058,6 +1067,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/{username}", func() { m.Group("/{reponame}", func() { m.Get("", repo.SetEditorconfigIfExists, repo.Home) + m.Get("/code", repo.SetEditorconfigIfExists, repo.Code) }, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes()) m.Group("/{reponame}", func() { diff --git a/services/forms/admin.go b/services/forms/admin.go index 5abef0550e..8a4aaaed50 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -41,7 +41,9 @@ type AdminEditUserForm struct { Email string `binding:"Required;Email;MaxSize(254)"` Password string `binding:"MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"` + Description string `binding:"MaxSize(1024)"` Location string `binding:"MaxSize(50)"` + LocationCoordinate string `binding:"MaxSize(255)"` MaxRepoCreation int Active bool Admin bool diff --git a/services/forms/org.go b/services/forms/org.go index dec2dbfa65..a5a0b06d7b 100644 --- a/services/forms/org.go +++ b/services/forms/org.go @@ -39,9 +39,10 @@ func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding type UpdateOrgSettingForm struct { Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"` FullName string `binding:"MaxSize(100)"` - Description string `binding:"MaxSize(255)"` + Description string `binding:"MaxSize(1024)"` Website string `binding:"ValidUrl;MaxSize(255)"` Location string `binding:"MaxSize(50)"` + LocationCoordinate string `binding:"MaxSize(255)"` Visibility structs.VisibleType MaxRepoCreation int RepoAdminChangeTeamAccess bool diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e78559912e..550375bd7a 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -74,7 +74,7 @@ type MigrateRepoForm struct { LFS bool `json:"lfs"` LFSEndpoint string `json:"lfs_endpoint"` Private bool `json:"private"` - Description string `json:"description" binding:"MaxSize(255)"` + Description string `json:"description" binding:"MaxSize(1024)"` Wiki bool `json:"wiki"` Milestones bool `json:"milestones"` Labels bool `json:"labels"` @@ -155,6 +155,7 @@ type RepoSettingForm struct { EnableTimetracker bool AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool + EnableIssueParents bool IsArchived bool // Signing Settings diff --git a/services/forms/user_form.go b/services/forms/user_form.go index a886e89f87..1ea967ca1b 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -245,7 +245,8 @@ type UpdateProfileForm struct { KeepEmailPrivate bool Website string `binding:"ValidSiteUrl;MaxSize(255)"` Location string `binding:"MaxSize(50)"` - Description string `binding:"MaxSize(255)"` + LocationCoordinate string `binding:"MaxSize(255)"` + Description string `binding:"MaxSize(1024)"` Visibility structs.VisibleType KeepActivityPrivate bool } @@ -273,7 +274,7 @@ const ( AvatarByMail string = "bymail" ) -// AvatarForm form for changing avatar +// AvatarForm form for changing avDescriptionatar type AvatarForm struct { Source string Avatar *multipart.FileHeader diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 2a27baf535..e31fe3cae6 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -186,6 +186,8 @@
{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}
{{.i18n.Tr "admin.config.default_enable_dependencies"}}
{{if .Service.DefaultEnableDependencies}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
+
{{.i18n.Tr "admin.config.default_enable_parents"}}
+
{{if .Service.DefaultEnableParents}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
{{.i18n.Tr "admin.config.active_code_lives"}}
{{.Service.ActiveCodeLives}} {{.i18n.Tr "tool.raw_minutes"}}
diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index 17bd2b936c..7487baf947 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -73,6 +73,10 @@

{{.i18n.Tr "admin.users.password_helper"}}

+
+ + +
@@ -81,6 +85,10 @@
+
+ + +
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 5ce1d0b888..ad8843f4b8 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -21,6 +21,7 @@ {{if .ShowMilestonesDashboardPage}}{{.i18n.Tr "milestones"}}{{end}} {{end}} {{.i18n.Tr "explore"}} + {{.i18n.Tr "map"}} {{else if .IsLandingPageOrganizations}} {{.i18n.Tr "explore"}} {{else}} diff --git a/templates/explore/organizations.tmpl b/templates/explore/organizations.tmpl index b2fee6c19a..0b9e001294 100644 --- a/templates/explore/organizations.tmpl +++ b/templates/explore/organizations.tmpl @@ -17,7 +17,7 @@
{{if .Location}} - {{svg "octicon-location"}} {{.Location}} + {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}}) {{end}} {{if and .Website}} {{svg "octicon-link"}} diff --git a/templates/explore/users.tmpl b/templates/explore/users.tmpl index dff4e8c8be..261ddec16f 100644 --- a/templates/explore/users.tmpl +++ b/templates/explore/users.tmpl @@ -12,7 +12,7 @@ {{.Name}} {{.FullName}}
{{if .Location}} - {{svg "octicon-location"}} {{.Location}} + {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}}) {{end}} {{if and $.ShowUserEmail .Email $.IsSigned (not .KeepEmailPrivate)}} {{svg "octicon-mail"}} diff --git a/templates/map/navbar.tmpl b/templates/map/navbar.tmpl new file mode 100644 index 0000000000..137f077657 --- /dev/null +++ b/templates/map/navbar.tmpl @@ -0,0 +1,18 @@ + diff --git a/templates/map/umap.tmpl b/templates/map/umap.tmpl new file mode 100644 index 0000000000..2112173afb --- /dev/null +++ b/templates/map/umap.tmpl @@ -0,0 +1,2862 @@ +{{template "base/head" .}} +
+ + + + + + + + + + + + + + +
+ + +
+{{template "base/footer" .}} diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index c72fc53de5..7ba5f95b46 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -12,7 +12,7 @@
{{if $.RenderedDescription}}

{{$.RenderedDescription|Str2html}}

{{end}}
- {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}}
{{end}} + {{if .Org.Location}}
{{svg "octicon-location"}} {{.Org.Location}} ({{.Org.LocationCoordinate}})
{{end}} {{if .Org.Website}}
{{svg "octicon-link"}} {{.Org.Website}}
{{end}}
diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 09bf5e0caa..a6d30dae48 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -36,6 +36,11 @@ +
+ + +
+
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 014623dc9a..4948d90379 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -2,7 +2,7 @@ {{with .Repository}}
-
+
{{$avatar := (repoAvatar . 32 "mr-3")}} {{if $avatar}} @@ -147,12 +147,12 @@
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}} + {{else if eq .Type 34}} +
+ {{svg "octicon-package-parents"}} + + {{avatar .Poster}} + + + {{.Poster.GetDisplayName}} + {{$.i18n.Tr "repo.issues.parent.added_parent" $createdStr | Safe}} + + {{if .ParentIssue}} + + {{end}} +
+ {{else if eq .Type 35}} +
+ {{svg "octicon-package-parents"}} + + {{avatar .Poster}} + + + {{.Poster.GetDisplayName}} + {{$.i18n.Tr "repo.issues.parent.removed_parent" $createdStr | Safe}} + + {{if .ParentIssue}} + + {{end}} +
{{end}} {{end}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 6198b6a621..5cc874c1e1 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -443,6 +443,131 @@
{{end}}
+ + {{if .Repository.IsParentsEnabled}} +
+ +
+ {{if (and (not .BlockedByParents) (not .BlockingParents))}} + {{.i18n.Tr "repo.issues.parent.title"}} +
+

+ {{if .Issue.IsPull}} + {{.i18n.Tr "repo.issues.parent.pr_no_parents"}} + {{else}} + {{.i18n.Tr "repo.issues.parent.issue_no_parents"}} + {{end}} +

+ {{end}} + + {{if .BlockingParents}} + + {{.i18n.Tr "repo.issues.parent.blocks_short"}} + +
+ {{range .BlockingParents}} +
+
+ + #{{.Issue.Index}} {{.Issue.Title | RenderEmoji}} + +
+ {{.Repository.OwnerName}}/{{.Repository.Name}} +
+
+
+ {{if and $.CanCreateIssueParents (not $.Repository.IsArchived)}} + + {{svg "octicon-trash" 16}} + + {{end}} +
+
+ {{end}} +
+ {{end}} + + {{if .BlockedByParents}} + + {{.i18n.Tr "repo.issues.parent.blocked_by_short"}} + +
+ {{range .BlockedByParents}} +
+
+ + #{{.Issue.Index}} {{.Issue.Title | RenderEmoji}} + +
+ {{.Repository.OwnerName}}/{{.Repository.Name}} +
+
+
+ {{if and $.CanCreateIssueParents (not $.Repository.IsArchived)}} + + {{svg "octicon-trash" 16}} + + {{end}} +
+
+ {{end}} +
+ {{end}} + + {{if and .CanCreateIssueParents (not .Repository.IsArchived)}} +
+
+ {{$.CsrfTokenHtml}} +
+ + +
+
+
+ {{end}} +
+ + {{if and .CanCreateIssueParents (not .Repository.IsArchived)}} + + + + {{end}} + {{end}} + {{if .Repository.IsDependenciesEnabled}}
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 4389b63559..2dc4ce17c6 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -329,6 +329,12 @@
+
+
+ + +
+
diff --git a/templates/repo/user_cards.tmpl b/templates/repo/user_cards.tmpl index 3526c698b9..0ec40116ca 100644 --- a/templates/repo/user_cards.tmpl +++ b/templates/repo/user_cards.tmpl @@ -16,7 +16,7 @@ {{if .Website}} {{svg "octicon-link"}} {{.Website}} {{else if .Location}} - {{svg "octicon-location"}} {{.Location}} + {{svg "octicon-location"}} {{.Location}} ({{.LocationCoordinate}}) {{else}} {{svg "octicon-clock"}} {{$.i18n.Tr "user.join_on"}} {{.CreatedUnix.FormatShort}} {{end}} diff --git a/templates/shared/issuelistfortree.tmpl b/templates/shared/issuelistfortree.tmpl new file mode 100644 index 0000000000..faf20964bc --- /dev/null +++ b/templates/shared/issuelistfortree.tmpl @@ -0,0 +1,144 @@ +
+ {{ $approvalCounts := .ApprovalCounts}} + {{range .Issues}} +
+
  • +
    + {{if $.CanWriteIssuesOrPulls}} +
    + + +
    + {{end}} +
    + {{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}} +
    +
    +
    +
    + {{RenderEmoji .Title}} + {{if .IsPull}} + {{if (index $.CommitStatus .PullRequest.ID)}} + {{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}} + {{end}} + {{end}} + + {{range .Labels}} + {{.Name | RenderEmoji}} + {{end}} + +
    +
    + + {{if eq $.listType "dashboard"}} + {{.Repo.FullName}}#{{.Index}} + {{else}} + #{{.Index}} + {{end}} + + {{ $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")}} + + {{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}} + + {{end}} + {{if .Ref}} + + {{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}} + + {{end}} + {{$tasks := .GetTasks}} + {{if gt $tasks 0}} + {{$tasksDone := .GetTasksDone}} + + {{svg "octicon-checklist" 14 "mr-2"}}{{$tasksDone}} / {{$tasks}} + + {{end}} + {{if ne .DeadlineUnix 0}} + + + {{svg "octicon-calendar" 14 "mr-2"}} + {{.DeadlineUnix.FormatShort}} + + + {{end}} + {{if .IsPull}} + {{$approveOfficial := call $approvalCounts .ID "approve"}} + {{$rejectOfficial := call $approvalCounts .ID "reject"}} + {{$waitingOfficial := call $approvalCounts .ID "waiting"}} + {{if gt $approveOfficial 0}} + + {{svg "octicon-check" 14 "mr-1"}} + {{$.i18n.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}} + + {{end}} + {{if gt $rejectOfficial 0}} + + {{svg "octicon-diff" 14 "mr-2"}} + {{$.i18n.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}} + + {{end}} + {{if gt $waitingOfficial 0}} + + {{svg "octicon-eye" 14 "mr-2"}} + {{$.i18n.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}} + + {{end}} + {{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}} + + {{svg "octicon-x" 14}} + {{$.i18n.TrN (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n" (len .PullRequest.ConflictedFiles)}} + + {{end}} + {{end}} +
    +
    +
    +
    + {{if .TotalTrackedTime}} + {{svg "octicon-clock" 16 "mr-2"}} + {{.TotalTrackedTime | Sec2Time}} + {{end}} +
    +
    + {{range .Assignees}} + + {{avatar .}} + + {{end}} +
    + +
    +
  • +
    + {{end}} +
    diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 791ada03fd..1d45d6e59c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15592,6 +15592,11 @@ "type": "boolean", "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": { "description": "Enable time tracking (Built-in issue tracker)", "type": "boolean", diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index e0a6b39121..d14a86fd17 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -20,7 +20,7 @@
      {{if .Owner.Location}} -
    • {{svg "octicon-location"}} {{.Owner.Location}}
    • +
    • {{svg "octicon-location"}} {{.Owner.Location}} ({{.Owner.LocationCoordinate}})
    • {{end}} {{if .ShowUserEmail }}
    • diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 9429db1ad6..18f5ed4b5c 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -46,6 +46,10 @@
    +
    + + +
    diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 9ee5e4f04e..a663e79880 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -121,6 +121,34 @@ export function initRepoIssueList() { 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) + }
    ${htmlEscape(issue.repository.full_name)}
    `, + value: issue.id, + }); + }); + return filteredResponse; + }, + cache: false, + }, + + fullTextSearch: true, + }); + + function excludeLabel(item) { const href = $(item).attr('href'); 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() { // Cancel inline code comment $(document).on('click', '.cancel-code-comment', (e) => { diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index f30345bfee..b8aea6268c 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -4,7 +4,7 @@ import {initCompImagePaste, initEasyMDEImagePaste} from './comp/ImagePaste.js'; import { initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete, - initRepoIssueComments, initRepoIssueDependencyDelete, + initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueParentDelete, initRepoIssueReferenceIssue, initRepoIssueStatusButton, initRepoIssueTitleEdit, initRepoIssueWipToggle, initRepoPullRequestMerge, initRepoPullRequestUpdate, @@ -516,6 +516,7 @@ export function initRepository() { initRepoIssueCommentDelete(); initRepoIssueDependencyDelete(); + initRepoIssueParentDelete(); initRepoIssueCodeCommentCancel(); initRepoIssueStatusButton(); initRepoPullRequestMerge(); diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 6cf70abdf7..897d94a401 100644 --- a/web_src/less/_repository.less +++ b/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 { font-size: 12px; }