diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2020-09-10 02:29:10 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-09 14:29:10 -0400 |
commit | e153cf07c34b1497906348df84a556851693c77f (patch) | |
tree | 79c642c2d04a907b0700c369108825d0b53085b9 | |
parent | 6483dbc8ec780dda22e21b605d1dc409826baff1 (diff) | |
download | gitea-e153cf07c34b1497906348df84a556851693c77f.tar.gz gitea-e153cf07c34b1497906348df84a556851693c77f.zip |
Add a migrate service type switch page (#12697)
* Add a migrat service type switch page
* Improve translations
* remove images
* Fix images
* remove extra create repo button on dashboard
* Follow reviewers' opinions
* Fix frontend lint
* Remove wrong submit file
* Fix tests
* Adjust the size of image
* Apply suggestions from code review
Co-authored-by: 赵智超 <1012112796@qq.com>
* Remove username and password from migration of github/gitlab
* Improve docs
* Improve interface docs
Co-authored-by: 赵智超 <1012112796@qq.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
-rw-r--r-- | integrations/repo_migrate_test.go | 8 | ||||
-rw-r--r-- | modules/structs/repo.go | 13 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 7 | ||||
-rw-r--r-- | public/img/svg/gitea-git.svg | 1 | ||||
-rw-r--r-- | public/img/svg/gitea-github.svg | 1 | ||||
-rw-r--r-- | public/img/svg/gitea-gitlab.svg | 1 | ||||
-rw-r--r-- | routers/repo/migrate.go | 173 | ||||
-rw-r--r-- | routers/repo/repo.go | 150 | ||||
-rw-r--r-- | routers/repo/view.go | 2 | ||||
-rw-r--r-- | templates/repo/migrate/git.tmpl | 103 | ||||
-rw-r--r-- | templates/repo/migrate/github.tmpl (renamed from templates/repo/migrate.tmpl) | 26 | ||||
-rw-r--r-- | templates/repo/migrate/gitlab.tmpl | 137 | ||||
-rw-r--r-- | templates/repo/migrate/migrate.tmpl | 23 | ||||
-rw-r--r-- | templates/repo/migrate/migrating.tmpl (renamed from templates/repo/migrating.tmpl) | 0 | ||||
-rw-r--r-- | templates/user/dashboard/repolist.tmpl | 8 | ||||
-rw-r--r-- | web_src/js/features/migration.js | 14 | ||||
-rw-r--r-- | web_src/less/_repository.less | 4 | ||||
-rw-r--r-- | web_src/less/themes/theme-arc-green.less | 16 | ||||
-rw-r--r-- | web_src/svg/gitea-git.svg | 1 | ||||
-rw-r--r-- | web_src/svg/gitea-github.svg | 1 | ||||
-rw-r--r-- | web_src/svg/gitea-gitlab.svg | 1 |
21 files changed, 481 insertions, 209 deletions
diff --git a/integrations/repo_migrate_test.go b/integrations/repo_migrate_test.go index a9970655ef..5a02b4ba03 100644 --- a/integrations/repo_migrate_test.go +++ b/integrations/repo_migrate_test.go @@ -5,15 +5,17 @@ package integrations import ( + "fmt" "net/http" "net/http/httptest" "testing" + "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) func testRepoMigrate(t testing.TB, session *TestSession, cloneAddr, repoName string) *httptest.ResponseRecorder { - req := NewRequest(t, "GET", "/repo/migrate") + req := NewRequest(t, "GET", fmt.Sprintf("/repo/migrate?service_type=%d", structs.PlainGitService)) // render plain git migration page resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) @@ -28,8 +30,8 @@ func testRepoMigrate(t testing.TB, session *TestSession, cloneAddr, repoName str "clone_addr": cloneAddr, "uid": uid, "repo_name": repoName, - }, - ) + "service": fmt.Sprintf("%d", structs.PlainGitService), + }) resp = session.MakeRequest(t, req, http.StatusFound) return resp diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 808d2ffbc8..f751c00789 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -5,6 +5,7 @@ package structs import ( + "strings" "time" ) @@ -205,17 +206,7 @@ const ( // Name represents the service type's name // WARNNING: the name have to be equal to that on goth's library func (gt GitServiceType) Name() string { - switch gt { - case GithubService: - return "github" - case GiteaService: - return "gitea" - case GitlabService: - return "gitlab" - case GogsService: - return "gogs" - } - return "" + return strings.ToLower(gt.Title()) } // Title represents the service type's proper title diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ce35375052..80308214dd 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -720,6 +720,7 @@ migrate_items_milestones = Milestones migrate_items_labels = Labels migrate_items_issues = Issues migrate_items_pullrequests = Pull Requests +migrate_items_merge_requests = Merge Requests migrate_items_releases = Releases migrate_repo = Migrate Repository migrate.clone_address = Migrate / Clone From URL @@ -729,11 +730,15 @@ migrate.permission_denied = You are not allowed to import local repositories. migrate.invalid_local_path = "The local path is invalid. It does not exist or is not a directory." migrate.failed = Migration failed: %v migrate.lfs_mirror_unsupported = Mirroring LFS objects is not supported - use 'git lfs fetch --all' and 'git lfs push --all' instead. -migrate.migrate_items_options = Authentication is needed to migrate items from a service that supports them. +migrate.migrate_items_options = Access Token is required to migrate additional items migrated_from = Migrated from <a href="%[1]s">%[2]s</a> migrated_from_fake = Migrated From %[1]s +migrate.migrate = Migrate From %s migrate.migrating = Migrating from <b>%s</b> ... migrate.migrating_failed = Migrating from <b>%s</b> failed. +migrate.github.description = Migrating data from Github.com or Github Enterprise. +migrate.git.description = Migrating or Mirroring git data from Git services +migrate.gitlab.description = Migrating data from GitLab.com or Self-Hosted gitlab server. mirror_from = mirror of forked_from = forked from diff --git a/public/img/svg/gitea-git.svg b/public/img/svg/gitea-git.svg new file mode 100644 index 0000000000..c716b1b283 --- /dev/null +++ b/public/img/svg/gitea-git.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 48 48" class="svg gitea-git" width="16" height="16" aria-hidden="true"><path fill="#F4511E" d="M42.2 22.1L25.9 5.8c-.5-.5-1.2-.8-1.9-.8s-1.4.3-1.9.8l-3.5 3.5 4.1 4.1c.4-.2.8-.3 1.3-.3 1.7 0 3 1.3 3 3 0 .5-.1.9-.3 1.3l4 4c.4-.2.8-.3 1.3-.3 1.7 0 3 1.3 3 3s-1.3 3-3 3-3-1.3-3-3c0-.5.1-.9.3-1.3l-4-4c-.1 0-.2.1-.3.1v10.4c1.2.4 2 1.5 2 2.8 0 1.7-1.3 3-3 3s-3-1.3-3-3c0-1.3.8-2.4 2-2.8V18.8c-1.2-.4-2-1.5-2-2.8 0-.5.1-.9.3-1.3l-4.1-4.1L5.8 22.1c-.5.5-.8 1.2-.8 1.9s.3 1.4.8 1.9l16.3 16.3c.5.5 1.2.8 1.9.8s1.4-.3 1.9-.8l16.3-16.3c.5-.5.8-1.2.8-1.9s-.3-1.4-.8-1.9z"/></svg>
\ No newline at end of file diff --git a/public/img/svg/gitea-github.svg b/public/img/svg/gitea-github.svg new file mode 100644 index 0000000000..0ed1d44b55 --- /dev/null +++ b/public/img/svg/gitea-github.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 64 64" class="svg gitea-github" width="16" height="16" aria-hidden="true"><linearGradient id="a" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#a)" d="M25.008 56.007c-.003-.368-.006-1.962-.009-3.454l-.003-1.55c-6.729.915-8.358-3.78-8.376-3.83-.934-2.368-2.211-3.045-2.266-3.073l-.124-.072c-.463-.316-1.691-1.157-1.342-2.263.315-.997 1.536-1.1 2.091-1.082 3.074.215 4.63 2.978 4.694 3.095 1.569 2.689 3.964 2.411 5.509 1.844.144-.688.367-1.32.659-1.878-4.956-.879-10.571-3.515-10.571-13.104 0-2.633.82-4.96 2.441-6.929-.362-1.206-.774-3.666.446-6.765l.174-.442.452-.144c.416-.137 2.688-.624 7.359 2.433a24.959 24.959 0 016.074-.759c2.115.01 4.158.265 6.09.759 4.667-3.058 6.934-2.565 7.351-2.433l.451.145.174.44c1.225 3.098.813 5.559.451 6.766 1.618 1.963 2.438 4.291 2.438 6.929 0 9.591-5.621 12.219-10.588 13.087.563 1.065.868 2.402.868 3.878 0 1.683-.007 7.204-.015 8.402l-2-.014c.008-1.196.015-6.708.015-8.389 0-2.442-.943-3.522-1.35-3.874l-1.73-1.497 2.274-.253c5.205-.578 10.525-2.379 10.525-11.341 0-2.33-.777-4.361-2.31-6.036l-.43-.469.242-.587c.166-.401.894-2.442-.043-5.291-.758.045-2.568.402-5.584 2.447l-.384.259-.445-.123c-1.863-.518-3.938-.796-6.001-.806-2.052.01-4.124.288-5.984.806l-.445.123-.383-.259c-3.019-2.044-4.833-2.404-5.594-2.449-.935 2.851-.206 4.892-.04 5.293l.242.587-.429.469c-1.536 1.681-2.314 3.712-2.314 6.036 0 8.958 5.31 10.77 10.504 11.361l2.252.256-1.708 1.49c-.372.325-1.03 1.112-1.254 2.727l-.075.549-.506.227c-1.321.592-5.839 2.162-8.548-2.485-.015-.025-.544-.945-1.502-1.557.646.639 1.433 1.673 2.068 3.287.066.19 1.357 3.622 7.28 2.339l1.206-.262.012 3.978c.003 1.487.006 3.076.009 3.444l-1.998.014z"/><linearGradient id="b" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#b)" d="M32 58C17.663 58 6 46.337 6 32S17.663 6 32 6s26 11.663 26 26-11.663 26-26 26zm0-50C18.767 8 8 18.767 8 32s10.767 24 24 24 24-10.767 24-24S45.233 8 32 8z"/></svg>
\ No newline at end of file diff --git a/public/img/svg/gitea-gitlab.svg b/public/img/svg/gitea-gitlab.svg new file mode 100644 index 0000000000..a72a378234 --- /dev/null +++ b/public/img/svg/gitea-gitlab.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 48 48" class="svg gitea-gitlab" width="16" height="16" aria-hidden="true"><path fill="#e53935" d="M24 43l-8-23h16z"/><path fill="#ff7043" d="M24 43l18-23H32z"/><path fill="#e53935" d="M37 5l5 15H32z"/><path fill="#ffa726" d="M24 43l18-23 3 8z"/><path fill="#ff7043" d="M24 43L6 20h10z"/><path fill="#e53935" d="M11 5L6 20h10z"/><path fill="#ffa726" d="M24 43L6 20l-3 8z"/></svg>
\ No newline at end of file diff --git a/routers/repo/migrate.go b/routers/repo/migrate.go new file mode 100644 index 0000000000..497f2ce36f --- /dev/null +++ b/routers/repo/migrate.go @@ -0,0 +1,173 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 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 ( + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/migrations" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/task" + "code.gitea.io/gitea/modules/util" +) + +const ( + tplMigrate base.TplName = "repo/migrate/migrate" +) + +// Migrate render migration of repository page +func Migrate(ctx *context.Context) { + ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...) + serviceType := ctx.QueryInt("service_type") + if serviceType == 0 { + ctx.HTML(200, tplMigrate) + return + } + + ctx.Data["Title"] = ctx.Tr("new_migrate") + ctx.Data["private"] = getRepoPrivate(ctx) + ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate + ctx.Data["DisableMirrors"] = setting.Repository.DisableMirrors + ctx.Data["mirror"] = ctx.Query("mirror") == "1" + ctx.Data["wiki"] = ctx.Query("wiki") == "1" + ctx.Data["milestones"] = ctx.Query("milestones") == "1" + ctx.Data["labels"] = ctx.Query("labels") == "1" + ctx.Data["issues"] = ctx.Query("issues") == "1" + ctx.Data["pull_requests"] = ctx.Query("pull_requests") == "1" + ctx.Data["releases"] = ctx.Query("releases") == "1" + ctx.Data["LFSActive"] = setting.LFS.StartServer + // Plain git should be first + ctx.Data["service"] = structs.GitServiceType(serviceType) + + ctxUser := checkContextUser(ctx, ctx.QueryInt64("org")) + if ctx.Written() { + return + } + ctx.Data["ContextUser"] = ctxUser + + ctx.HTML(200, base.TplName("repo/migrate/"+structs.GitServiceType(serviceType).Name())) +} + +func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) { + switch { + case migrations.IsRateLimitError(err): + ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form) + case migrations.IsTwoFactorAuthError(err): + ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form) + case models.IsErrReachLimitOfRepo(err): + ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form) + case models.IsErrRepoAlreadyExist(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form) + case models.IsErrNameReserved(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form) + case models.IsErrNamePatternNotAllowed(err): + ctx.Data["Err_RepoName"] = true + ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) + default: + remoteAddr, _ := form.ParseRemoteAddr(owner) + err = util.URLSanitizedError(err, remoteAddr) + if strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "Bad credentials") || + strings.Contains(err.Error(), "could not read Username") { + ctx.Data["Err_Auth"] = true + ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tpl, form) + } else if strings.Contains(err.Error(), "fatal:") { + ctx.Data["Err_CloneAddr"] = true + ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tpl, form) + } else { + ctx.ServerError(name, err) + } + } +} + +// MigratePost response for migrating from external git repository +func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { + ctx.Data["Title"] = ctx.Tr("new_migrate") + // Plain git should be first + ctx.Data["service"] = form.Service + ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...) + + ctxUser := checkContextUser(ctx, form.UID) + if ctx.Written() { + return + } + ctx.Data["ContextUser"] = ctxUser + + if ctx.HasError() { + ctx.HTML(200, tplMigrate) + return + } + + remoteAddr, err := form.ParseRemoteAddr(ctx.User) + if err != nil { + if models.IsErrInvalidCloneAddr(err) { + ctx.Data["Err_CloneAddr"] = true + addrErr := err.(models.ErrInvalidCloneAddr) + switch { + case addrErr.IsURLError: + ctx.RenderWithErr(ctx.Tr("form.url_error"), tplMigrate, &form) + case addrErr.IsPermissionDenied: + ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied"), tplMigrate, &form) + case addrErr.IsInvalidPath: + ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tplMigrate, &form) + default: + ctx.ServerError("Unknown error", err) + } + } else { + ctx.ServerError("ParseRemoteAddr", err) + } + return + } + + var opts = migrations.MigrateOptions{ + OriginalURL: form.CloneAddr, + GitServiceType: structs.GitServiceType(form.Service), + CloneAddr: remoteAddr, + RepoName: form.RepoName, + Description: form.Description, + Private: form.Private || setting.Repository.ForcePrivate, + Mirror: form.Mirror && !setting.Repository.DisableMirrors, + AuthUsername: form.AuthUsername, + AuthPassword: form.AuthPassword, + AuthToken: form.AuthToken, + Wiki: form.Wiki, + Issues: form.Issues, + Milestones: form.Milestones, + Labels: form.Labels, + Comments: form.Issues || form.PullRequests, + PullRequests: form.PullRequests, + Releases: form.Releases, + } + if opts.Mirror { + opts.Issues = false + opts.Milestones = false + opts.Labels = false + opts.Comments = false + opts.PullRequests = false + opts.Releases = false + } + + err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName) + if err != nil { + handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) + return + } + + err = task.MigrateRepository(ctx.User, ctxUser, opts) + if err == nil { + ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + opts.RepoName) + return + } + + handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) +} diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 5fc081a6f6..9a4fbfa130 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -17,19 +17,14 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/migrations" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/task" - "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" "github.com/unknwon/com" ) const ( - tplCreate base.TplName = "repo/create" - tplMigrate base.TplName = "repo/migrate" + tplCreate base.TplName = "repo/create" ) // MustBeNotEmpty render when a repo is a empty git dir @@ -254,149 +249,6 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { handleCreateError(ctx, ctxUser, err, "CreatePost", tplCreate, &form) } -// Migrate render migration of repository page -func Migrate(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("new_migrate") - ctx.Data["private"] = getRepoPrivate(ctx) - ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate - ctx.Data["DisableMirrors"] = setting.Repository.DisableMirrors - ctx.Data["mirror"] = ctx.Query("mirror") == "1" - ctx.Data["wiki"] = ctx.Query("wiki") == "1" - ctx.Data["milestones"] = ctx.Query("milestones") == "1" - ctx.Data["labels"] = ctx.Query("labels") == "1" - ctx.Data["issues"] = ctx.Query("issues") == "1" - ctx.Data["pull_requests"] = ctx.Query("pull_requests") == "1" - ctx.Data["releases"] = ctx.Query("releases") == "1" - ctx.Data["LFSActive"] = setting.LFS.StartServer - // Plain git should be first - ctx.Data["service"] = structs.PlainGitService - ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...) - - ctxUser := checkContextUser(ctx, ctx.QueryInt64("org")) - if ctx.Written() { - return - } - ctx.Data["ContextUser"] = ctxUser - - ctx.HTML(200, tplMigrate) -} - -func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) { - switch { - case migrations.IsRateLimitError(err): - ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form) - case migrations.IsTwoFactorAuthError(err): - ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form) - case models.IsErrReachLimitOfRepo(err): - ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form) - case models.IsErrRepoAlreadyExist(err): - ctx.Data["Err_RepoName"] = true - ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form) - case models.IsErrNameReserved(err): - ctx.Data["Err_RepoName"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form) - case models.IsErrNamePatternNotAllowed(err): - ctx.Data["Err_RepoName"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) - default: - remoteAddr, _ := form.ParseRemoteAddr(owner) - err = util.URLSanitizedError(err, remoteAddr) - if strings.Contains(err.Error(), "Authentication failed") || - strings.Contains(err.Error(), "Bad credentials") || - strings.Contains(err.Error(), "could not read Username") { - ctx.Data["Err_Auth"] = true - ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tpl, form) - } else if strings.Contains(err.Error(), "fatal:") { - ctx.Data["Err_CloneAddr"] = true - ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tpl, form) - } else { - ctx.ServerError(name, err) - } - } -} - -// MigratePost response for migrating from external git repository -func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { - ctx.Data["Title"] = ctx.Tr("new_migrate") - // Plain git should be first - ctx.Data["service"] = structs.PlainGitService - ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...) - - ctxUser := checkContextUser(ctx, form.UID) - if ctx.Written() { - return - } - ctx.Data["ContextUser"] = ctxUser - - if ctx.HasError() { - ctx.HTML(200, tplMigrate) - return - } - - remoteAddr, err := form.ParseRemoteAddr(ctx.User) - if err != nil { - if models.IsErrInvalidCloneAddr(err) { - ctx.Data["Err_CloneAddr"] = true - addrErr := err.(models.ErrInvalidCloneAddr) - switch { - case addrErr.IsURLError: - ctx.RenderWithErr(ctx.Tr("form.url_error"), tplMigrate, &form) - case addrErr.IsPermissionDenied: - ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied"), tplMigrate, &form) - case addrErr.IsInvalidPath: - ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tplMigrate, &form) - default: - ctx.ServerError("Unknown error", err) - } - } else { - ctx.ServerError("ParseRemoteAddr", err) - } - return - } - - var opts = migrations.MigrateOptions{ - OriginalURL: form.CloneAddr, - GitServiceType: structs.GitServiceType(form.Service), - CloneAddr: remoteAddr, - RepoName: form.RepoName, - Description: form.Description, - Private: form.Private || setting.Repository.ForcePrivate, - Mirror: form.Mirror && !setting.Repository.DisableMirrors, - AuthUsername: form.AuthUsername, - AuthPassword: form.AuthPassword, - AuthToken: form.AuthToken, - Wiki: form.Wiki, - Issues: form.Issues, - Milestones: form.Milestones, - Labels: form.Labels, - Comments: true, - PullRequests: form.PullRequests, - Releases: form.Releases, - } - if opts.Mirror { - opts.Issues = false - opts.Milestones = false - opts.Labels = false - opts.Comments = false - opts.PullRequests = false - opts.Releases = false - } - - err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName) - if err != nil { - handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) - return - } - - err = task.MigrateRepository(ctx.User, ctxUser, opts) - if err == nil { - ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + opts.RepoName) - return - } - - handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) -} - // Action response for actions to a repository func Action(ctx *context.Context) { var err error diff --git a/routers/repo/view.go b/routers/repo/view.go index a05c0b1366..3e2a57415d 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -34,7 +34,7 @@ const ( tplRepoHome base.TplName = "repo/home" tplWatchers base.TplName = "repo/watchers" tplForks base.TplName = "repo/forks" - tplMigrating base.TplName = "repo/migrating" + tplMigrating base.TplName = "repo/migrate/migrating" ) type namedBlob struct { diff --git a/templates/repo/migrate/git.tmpl b/templates/repo/migrate/git.tmpl new file mode 100644 index 0000000000..34a1c7bd0d --- /dev/null +++ b/templates/repo/migrate/git.tmpl @@ -0,0 +1,103 @@ +{{template "base/head" .}} +<div class="repository new migrate"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <h3 class="ui top attached header"> + {{.i18n.Tr "repo.migrate.migrate" .service.Title}} + <input id="service_type" type="hidden" name="service" value="{{.service}}"> + </h3> + <div class="ui attached segment"> + {{template "base/alert" .}} + <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> + <label for="clone_addr">{{.i18n.Tr "repo.migrate.clone_address"}}</label> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <span class="help"> + {{.i18n.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{.i18n.Tr "repo.migrate.clone_local_path"}}{{end}} + {{if .LFSActive}}<br/>{{.i18n.Tr "repo.migrate.lfs_mirror_unsupported"}}{{end}} + </span> + </div> + <div class="inline field {{if .Err_Auth}}error{{end}}"> + <label for="auth_username">{{.i18n.Tr "username"}}</label> + <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}> + </div> + <input class="fake" type="password"> + <div class="inline field {{if .Err_Auth}}error{{end}}"> + <label for="auth_password">{{.i18n.Tr "password"}}</label> + <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}"> + </div> + + <div class="inline field"> + <label>{{.i18n.Tr "repo.migrate_options"}}</label> + <div class="ui checkbox"> + {{if .DisableMirrors}} + <input id="mirror" name="mirror" type="checkbox" readonly> + <label>{{.i18n.Tr "repo.migrate_options_mirror_disabled"}}</label> + {{else}} + <input id="mirror" name="mirror" type="checkbox" {{if .mirror}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_options_mirror_helper" | Safe}}</label> + {{end}} + </div> + </div> + + <div class="ui divider"></div> + + <div class="inline required field {{if .Err_Owner}}error{{end}}"> + <label>{{.i18n.Tr "repo.owner"}}</label> + <div class="ui selection owner dropdown"> + <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> + <span class="text" title="{{.ContextUser.Name}}"> + <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> + {{.ContextUser.ShortName 20}} + </span> + <i class="dropdown icon"></i> + <div class="menu" title="{{.SignedUser.Name}}"> + <div class="item" data-value="{{.SignedUser.ID}}"> + <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> + {{.SignedUser.ShortName 20}} + </div> + {{range .Orgs}} + <div class="item" data-value="{{.ID}}" title="{{.Name}}"> + <img class="ui mini image" src="{{.RelAvatarLink}}"> + {{.ShortName 20}} + </div> + {{end}} + </div> + </div> + </div> + + <div class="inline required field {{if .Err_RepoName}}error{{end}}"> + <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> + <input id="repo_name" name="repo_name" value="{{.repo_name}}" required> + </div> + <div class="inline field"> + <label>{{.i18n.Tr "repo.visibility"}}</label> + <div class="ui checkbox"> + {{if .IsForcedPrivate}} + <input name="private" type="checkbox" checked readonly> + <label>{{.i18n.Tr "repo.visibility_helper_forced" | Safe}}</label> + {{else}} + <input name="private" type="checkbox" {{if .private}}checked{{end}}> + <label>{{.i18n.Tr "repo.visibility_helper" | Safe}}</label> + {{end}} + </div> + </div> + <div class="inline field {{if .Err_Description}}error{{end}}"> + <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> + <textarea id="description" name="description">{{.description}}</textarea> + </div> + + <div class="inline field"> + <label></label> + <button class="ui green button"> + {{.i18n.Tr "repo.migrate_repo"}} + </button> + <a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> + </div> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/repo/migrate.tmpl b/templates/repo/migrate/github.tmpl index d5a31a6800..cf84ad39e0 100644 --- a/templates/repo/migrate.tmpl +++ b/templates/repo/migrate/github.tmpl @@ -5,7 +5,8 @@ <form class="ui form" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <h3 class="ui top attached header"> - {{.i18n.Tr "new_migrate"}} + {{.i18n.Tr "repo.migrate.migrate" .service.Title}} + <input id="service_type" type="hidden" name="service" value="{{.service}}"> </h3> <div class="ui attached segment"> {{template "base/alert" .}} @@ -18,31 +19,10 @@ </span> </div> - <div class="inline field"> - <label>{{.i18n.Tr "repo.migrate_service"}}</label> - <div class="ui selection dropdown"> - <input id="service_type" type="hidden" name="service" value="{{.service}}"> - <div class="default text"></div> - <i class="dropdown icon"></i> - <div class="menu"> - {{range .Services}} - <div id="service-{{.}}" class="item" data-token="{{.TokenAuth}}" data-value="{{.}}">{{.Title}}</div> - {{end}} - </div> - </div> - </div> - <div class="inline field {{if .Err_Auth}}error{{end}}"> - <label for="auth_username">{{.i18n.Tr "username"}}</label> - <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}> - </div> - <input class="fake" type="password"> - <div class="inline field {{if .Err_Auth}}error{{end}}"> - <label for="auth_password">{{.i18n.Tr "password"}}</label> - <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}"> - </div> <div class="inline field {{if .Err_Auth}}error{{end}}"> <label for="auth_token">{{.i18n.Tr "access_token"}}</label> <input id="auth_token" name="auth_token" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}> + <a target=”_blank” href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token">{{svg "octicon-question" 16}}</a> </div> <div class="inline field"> diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl new file mode 100644 index 0000000000..427a1e05df --- /dev/null +++ b/templates/repo/migrate/gitlab.tmpl @@ -0,0 +1,137 @@ +{{template "base/head" .}} +<div class="repository new migrate"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <h3 class="ui top attached header"> + {{.i18n.Tr "repo.migrate.migrate" .service.Title}} + <input id="service_type" type="hidden" name="service" value="{{.service}}"> + </h3> + <div class="ui attached segment"> + {{template "base/alert" .}} + <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> + <label for="clone_addr">{{.i18n.Tr "repo.migrate.clone_address"}}</label> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <span class="help"> + {{.i18n.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{.i18n.Tr "repo.migrate.clone_local_path"}}{{end}} + {{if .LFSActive}}<br/>{{.i18n.Tr "repo.migrate.lfs_mirror_unsupported"}}{{end}} + </span> + </div> + + <div class="inline field {{if .Err_Auth}}error{{end}}"> + <label for="auth_token">{{.i18n.Tr "access_token"}}</label> + <input id="auth_token" name="auth_token" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}> + <a target=”_blank” href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">{{svg "octicon-question" 16}}</a> + </div> + + <div class="inline field"> + <label>{{.i18n.Tr "repo.migrate_options"}}</label> + <div class="ui checkbox"> + {{if .DisableMirrors}} + <input id="mirror" name="mirror" type="checkbox" readonly> + <label>{{.i18n.Tr "repo.migrate_options_mirror_disabled"}}</label> + {{else}} + <input id="mirror" name="mirror" type="checkbox" {{if .mirror}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_options_mirror_helper" | Safe}}</label> + {{end}} + </div> + </div> + + <span class="help">{{.i18n.Tr "repo.migrate.migrate_items_options"}}</span> + <div id="migrate_items"> + <div class="inline field"> + <label>{{.i18n.Tr "repo.migrate_items"}}</label> + <div class="ui checkbox"> + <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_wiki" | Safe}}</label> + </div> + <div class="ui checkbox"> + <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_milestones" | Safe}}</label> + </div> + </div> + <div class="inline field"> + <label></label> + <div class="ui checkbox"> + <input name="labels" type="checkbox" {{if .labels}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_labels" | Safe}}</label> + </div> + <div class="ui checkbox"> + <input name="issues" type="checkbox" {{if .issues}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_issues" | Safe}}</label> + </div> + </div> + <div class="inline field"> + <label></label> + <div class="ui checkbox"> + <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_merge_requests" | Safe}}</label> + </div> + <div class="ui checkbox"> + <input name="releases" type="checkbox" {{if .releases}}checked{{end}}> + <label>{{.i18n.Tr "repo.migrate_items_releases" | Safe}}</label> + </div> + </div> + </div> + + <div class="ui divider"></div> + + <div class="inline required field {{if .Err_Owner}}error{{end}}"> + <label>{{.i18n.Tr "repo.owner"}}</label> + <div class="ui selection owner dropdown"> + <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> + <span class="text" title="{{.ContextUser.Name}}"> + <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> + {{.ContextUser.ShortName 20}} + </span> + <i class="dropdown icon"></i> + <div class="menu" title="{{.SignedUser.Name}}"> + <div class="item" data-value="{{.SignedUser.ID}}"> + <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> + {{.SignedUser.ShortName 20}} + </div> + {{range .Orgs}} + <div class="item" data-value="{{.ID}}" title="{{.Name}}"> + <img class="ui mini image" src="{{.RelAvatarLink}}"> + {{.ShortName 20}} + </div> + {{end}} + </div> + </div> + </div> + + <div class="inline required field {{if .Err_RepoName}}error{{end}}"> + <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> + <input id="repo_name" name="repo_name" value="{{.repo_name}}" required> + </div> + <div class="inline field"> + <label>{{.i18n.Tr "repo.visibility"}}</label> + <div class="ui checkbox"> + {{if .IsForcedPrivate}} + <input name="private" type="checkbox" checked readonly> + <label>{{.i18n.Tr "repo.visibility_helper_forced" | Safe}}</label> + {{else}} + <input name="private" type="checkbox" {{if .private}}checked{{end}}> + <label>{{.i18n.Tr "repo.visibility_helper" | Safe}}</label> + {{end}} + </div> + </div> + <div class="inline field {{if .Err_Description}}error{{end}}"> + <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> + <textarea id="description" name="description">{{.description}}</textarea> + </div> + + <div class="inline field"> + <label></label> + <button class="ui green button"> + {{.i18n.Tr "repo.migrate_repo"}} + </button> + <a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> + </div> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl new file mode 100644 index 0000000000..1521620b0e --- /dev/null +++ b/templates/repo/migrate/migrate.tmpl @@ -0,0 +1,23 @@ +{{template "base/head" .}} +<div class="repository new migrate"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <div class="ui three stackable cards"> + {{range .Services}} + <div class="ui card"> + <a class="image" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}"> + {{svg (Printf "gitea-%s" .Name) 184}} + </a> + <div class="content"> + <a class="header" href="{{AppSubUrl}}/repo/migrate?service_type={{.}}">{{.Title}}</a> + <div class="description"> + {{(Printf "repo.migrate.%s.description" .Name) | $.i18n.Tr }} + </div> + </div> + </div> + {{end}} + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/repo/migrating.tmpl b/templates/repo/migrate/migrating.tmpl index 0057325e91..0057325e91 100644 --- a/templates/repo/migrating.tmpl +++ b/templates/repo/migrate/migrating.tmpl diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index ca055e9d87..ce4a97a36f 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -25,14 +25,6 @@ <div v-show="tab === 'repos'" class="ui tab active list dashboard-repos"> <h4 class="ui top attached header"> {{.i18n.Tr "home.my_repos"}} <span class="ui grey label">${reposTotalCount}</span> - {{if or (not .ContextUser.IsOrganization) .IsOrganizationOwner}} - <div class="ui right"> - <a class="poping up" :href="suburl + '/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}'" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center"> - <i class="plus icon"></i> - <span class="sr-only">{{.i18n.Tr "new_repo"}}</span> - </a> - </div> - {{end}} </h4> <div class="ui attached secondary segment repos-search"> <div class="ui fluid right action left icon input" :class="{loading: isLoading}"> diff --git a/web_src/js/features/migration.js b/web_src/js/features/migration.js index e4c306307f..7a406616fd 100644 --- a/web_src/js/features/migration.js +++ b/web_src/js/features/migration.js @@ -7,7 +7,6 @@ const $items = $('#migrate_items').find('.field'); export default function initMigration() { checkAuth(); - $service.on('change', checkAuth); $user.on('keyup', () => {checkItems(false)}); $pass.on('keyup', () => {checkItems(false)}); $token.on('keyup', () => {checkItems(true)}); @@ -23,19 +22,8 @@ export default function initMigration() { function checkAuth() { const serviceType = $service.val(); - const tokenAuth = $(`#service-${serviceType}`).data('token'); - if (tokenAuth) { - $user.parent().addClass('disabled'); - $pass.parent().addClass('disabled'); - $token.parent().removeClass('disabled'); - } else { - $user.parent().removeClass('disabled'); - $pass.parent().removeClass('disabled'); - $token.parent().addClass('disabled'); - } - - checkItems(tokenAuth); + checkItems(serviceType !== 1); } function checkItems(tokenAuth) { diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 2a8d3b5693..b3a5f60ed1 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -3200,3 +3200,7 @@ td.blob-excerpt { .select-project .item .svg { margin-right: .5rem; } + +.migrate .cards .card { + text-align: center; +} diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less index 9b78d2e67b..0de6e1c710 100644 --- a/web_src/less/themes/theme-arc-green.less +++ b/web_src/less/themes/theme-arc-green.less @@ -1994,3 +1994,19 @@ footer .container .links > * { border: 1px solid rgba(121, 71, 66, .5) !important; border-bottom: none !important; } + +.migrate .cards .card { + text-align: center; +} + +.migrate .cards .card .content a { + color: rgb(158, 158, 158) !important; +} + +.migrate .cards .card .content a:hover { + color: rgb(255, 255, 255) !important; +} + +.migrate .cards .card .content .description { + color: rgb(158, 158, 158); +} diff --git a/web_src/svg/gitea-git.svg b/web_src/svg/gitea-git.svg new file mode 100644 index 0000000000..fee8870daf --- /dev/null +++ b/web_src/svg/gitea-git.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="64px" height="64px"><path fill="#F4511E" d="M42.2,22.1L25.9,5.8C25.4,5.3,24.7,5,24,5c0,0,0,0,0,0c-0.7,0-1.4,0.3-1.9,0.8l-3.5,3.5l4.1,4.1c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3c0,0.5-0.1,0.9-0.3,1.3l4,4c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3s-1.3,3-3,3c-1.7,0-3-1.3-3-3c0-0.5,0.1-0.9,0.3-1.3l-4-4c-0.1,0-0.2,0.1-0.3,0.1v10.4c1.2,0.4,2,1.5,2,2.8c0,1.7-1.3,3-3,3s-3-1.3-3-3c0-1.3,0.8-2.4,2-2.8V18.8c-1.2-0.4-2-1.5-2-2.8c0-0.5,0.1-0.9,0.3-1.3l-4.1-4.1L5.8,22.1C5.3,22.6,5,23.3,5,24c0,0.7,0.3,1.4,0.8,1.9l16.3,16.3c0,0,0,0,0,0c0.5,0.5,1.2,0.8,1.9,0.8s1.4-0.3,1.9-0.8l16.3-16.3c0.5-0.5,0.8-1.2,0.8-1.9C43,23.3,42.7,22.6,42.2,22.1z"/></svg>
\ No newline at end of file diff --git a/web_src/svg/gitea-github.svg b/web_src/svg/gitea-github.svg new file mode 100644 index 0000000000..918a35ecf6 --- /dev/null +++ b/web_src/svg/gitea-github.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64px" height="64px"><linearGradient id="KpzH_ttTMIjq8dhx1zD2pa" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pa)" d="M25.008,56.007c-0.003-0.368-0.006-1.962-0.009-3.454l-0.003-1.55 c-6.729,0.915-8.358-3.78-8.376-3.83c-0.934-2.368-2.211-3.045-2.266-3.073l-0.124-0.072c-0.463-0.316-1.691-1.157-1.342-2.263 c0.315-0.997,1.536-1.1,2.091-1.082c3.074,0.215,4.63,2.978,4.694,3.095c1.569,2.689,3.964,2.411,5.509,1.844 c0.144-0.688,0.367-1.32,0.659-1.878C20.885,42.865,15.27,40.229,15.27,30.64c0-2.633,0.82-4.96,2.441-6.929 c-0.362-1.206-0.774-3.666,0.446-6.765l0.174-0.442l0.452-0.144c0.416-0.137,2.688-0.624,7.359,2.433 c1.928-0.494,3.969-0.749,6.074-0.759c2.115,0.01,4.158,0.265,6.09,0.759c4.667-3.058,6.934-2.565,7.351-2.433l0.451,0.145 l0.174,0.44c1.225,3.098,0.813,5.559,0.451,6.766c1.618,1.963,2.438,4.291,2.438,6.929c0,9.591-5.621,12.219-10.588,13.087 c0.563,1.065,0.868,2.402,0.868,3.878c0,1.683-0.007,7.204-0.015,8.402l-2-0.014c0.008-1.196,0.015-6.708,0.015-8.389 c0-2.442-0.943-3.522-1.35-3.874l-1.73-1.497l2.274-0.253c5.205-0.578,10.525-2.379,10.525-11.341c0-2.33-0.777-4.361-2.31-6.036 l-0.43-0.469l0.242-0.587c0.166-0.401,0.894-2.442-0.043-5.291c-0.758,0.045-2.568,0.402-5.584,2.447l-0.384,0.259l-0.445-0.123 c-1.863-0.518-3.938-0.796-6.001-0.806c-2.052,0.01-4.124,0.288-5.984,0.806l-0.445,0.123l-0.383-0.259 c-3.019-2.044-4.833-2.404-5.594-2.449c-0.935,2.851-0.206,4.892-0.04,5.293l0.242,0.587l-0.429,0.469 c-1.536,1.681-2.314,3.712-2.314,6.036c0,8.958,5.31,10.77,10.504,11.361l2.252,0.256l-1.708,1.49 c-0.372,0.325-1.03,1.112-1.254,2.727l-0.075,0.549l-0.506,0.227c-1.321,0.592-5.839,2.162-8.548-2.485 c-0.015-0.025-0.544-0.945-1.502-1.557c0.646,0.639,1.433,1.673,2.068,3.287c0.066,0.19,1.357,3.622,7.28,2.339l1.206-0.262 l0.012,3.978c0.003,1.487,0.006,3.076,0.009,3.444L25.008,56.007z"/><linearGradient id="KpzH_ttTMIjq8dhx1zD2pb" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pb)" d="M32,58C17.663,58,6,46.337,6,32S17.663,6,32,6s26,11.663,26,26S46.337,58,32,58z M32,8 C18.767,8,8,18.767,8,32s10.767,24,24,24s24-10.767,24-24S45.233,8,32,8z"/></svg>
\ No newline at end of file diff --git a/web_src/svg/gitea-gitlab.svg b/web_src/svg/gitea-gitlab.svg new file mode 100644 index 0000000000..6a9c8a80f4 --- /dev/null +++ b/web_src/svg/gitea-gitlab.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="64px" height="64px"><path fill="#e53935" d="M24 43L16 20 32 20z"/><path fill="#ff7043" d="M24 43L42 20 32 20z"/><path fill="#e53935" d="M37 5L42 20 32 20z"/><path fill="#ffa726" d="M24 43L42 20 45 28z"/><path fill="#ff7043" d="M24 43L6 20 16 20z"/><path fill="#e53935" d="M11 5L6 20 16 20z"/><path fill="#ffa726" d="M24 43L6 20 3 28z"/></svg>
\ No newline at end of file |