diff --git a/.gitignore b/.gitignore index 572212e0e..9f34fea2a 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,6 @@ prime/ # Make evidence files /.make_evidence + +/templates/home_bak.tmpl +/==bak \ No newline at end of file diff --git a/custom/public/css/git.openi.css b/custom/public/css/git.openi.css new file mode 100644 index 000000000..ab5c92c01 --- /dev/null +++ b/custom/public/css/git.openi.css @@ -0,0 +1,184 @@ +.bg-gray{ background-color:#F8F8F8!important;} +.am-mb-0{ margin-bottom: 0 !important;} +.am-mb-05{ margin-bottom: 0.5rem !important;} +.am-mb-10{ margin-bottom: 1.0rem !important;} +.am-mb-15{ margin-bottom: 1.5rem !important;} +.am-mb-20{ margin-bottom: 2.0rem !important;} +.am-mb-30{ margin-bottom: 3.0rem !important;} +.am-mt-0{ margin-top: 0 !important;} +.am-mt-05{ margin-top: 0.5rem !important;} +.am-mt-10{ margin-top: 1.0rem !important;} +.am-mt-15{ margin-top: 1.5rem !important;} +.am-mt-20{ margin-top: 2.0rem !important;} +.am-mt-30{ margin-top: 3.0rem !important;} +.am-pb-0{ padding-bottom: 0 !important;} +.am-pb-05{ padding-bottom: 0.5rem !important;} +.am-pb-10{ padding-bottom: 1.0rem !important;} +.am-pb-15{ padding-bottom: 1.5rem !important;} +.am-pb-20{ padding-bottom: 2.0rem !important;} +.am-pb-30{ padding-bottom: 3.0rem !important;} +.am-pt-0{ padding-top: 0 !important;} +.am-pt-05{ padding-top: 0.5rem !important;} +.am-pt-10{ padding-top: 1.0rem !important;} +.am-pt-15{ padding-top: 1.5rem !important;} +.am-pt-20{ padding-top: 2.0rem !important;} +.am-pt-30{ padding-top: 3.0rem !important;} +.am-pl-30{ padding-left: 3.0rem !important;} +.am-ml-30{ margin-left: 3.0rem !important;} +.am-pr-30{ padding-right: 3.0rem !important;} +.am-mr-30{ margin-right: 3.0rem !important;} +.am-lh-18{ line-height: 1.8em;} + +.opacity5{ opacity:0.5;} +.radius15{ border-radius:1.5rem !important; } +.radius10{ border-radius:1.0rem !important; } +.radius5{ border-radius:0.5rem !important; } +.am-shadow-1{ + -webkit-box-shadow: 0 1px 2px 0 rgba(34,36,38,.15); + box-shadow: 0 1px 2px 0 rgba(34,36,38,.15); +} +.am-shadow-2{ + -webkit-box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); + box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); +} + + +.ui.secondary.hometop.segment{ + background: #DFE9F0; + padding-top: 0; + border: none; + margin-bottom: 90px; +} +.ui.secondary.hometop.segment #navbar{ + z-index: 10; +} + +.hometop .ui.secondary.menu .active.item{ + color: #000; + background:none; + border-bottom: 1px solid #000; +} +.hometop .following.bar #navbar .brand{ + padding-top: .78571429em; +} +.hometop .following.bar .brand .ui.mini.image { + height: 50px; +} + +.homebanner{ + position: relative; + padding: 100px 32px 80px; + z-index: 9; +} +.homebanner .ui.header .sub.header{ + color: #3291F8; +} +.bannerpic{ + position: absolute; + right: 50px; + bottom: -64px; + width: 560px; +} +.ui[class*="very padded"].segment.i-code{ + padding-left: 6.0rem; +} +.i-code > .ui.blue.header{ + color: #1678c2!important; +} +.i-code h2{ + position: relative; +} +.i-code h2::before { + content: ""; + position: absolute; + left: calc(-4.0rem + 6px); + top: 0.8rem; + width: 18px; + height: 18px; + border: 2px solid #505559; + border-radius: 50%; + background: #1b1c1d; + z-index: 9; +} +.i-code h2.am-bw::before{ + background: #FFF; +} +.i-code-pic{ + position: relative; +} +.i-code-pic > img{ + margin-bottom: -3.0rem; +} +.i-env .ui.cards>.card>.image{ + background: none; +} +.i-env .ui.cards>.card>.content{ + border-top: none; +} +.leftline01{ + position: absolute; + left: 3.0rem; + top: 0; + bottom: 0; + border-left: 2px solid #505559; + border-bottom: 2px solid #505559; + border-radius: 0 0 0 2.0rem; + width: 2.0rem; +} +.leftline02{ + position: absolute; + left: 5rem; + top: calc(-5.0rem - 2px); + border-top: 2px solid #505559; + border-right: 2px solid #505559; + border-radius: 0 2.0rem 0 0; + width: 17.5rem; + height: 6.0rem; +} + +@media only screen and (max-width: 767px) { + .am-mt-30{ margin-top: 1.5rem !important;} + .ui.secondary.hometop.segment{ + margin-bottom: 2.0rem; + } + .bannerpic, .i-code-pic{ + display: none; + } + .i-code h2::before { + left: calc(-5.0rem + 6px); + } + .i-code h2.am-bw::before{ + left: calc(-4.0rem + 6px); + } + .leftline01{ + width: calc(50% - 4.0rem); + } + .leftline02{ + left: calc(50% - 1.0rem); + top: calc(-3.5rem - 2px); + } +} + +@media only screen and (min-width: 768px) and (max-width: 991px) { + .bannerpic, .i-code-pic{ + display: none; + } + .i-code h2::before { + left: calc(-5.0rem + 6px); + } + .i-code h2.am-bw::before{ + left: calc(-4.0rem + 6px); + } +} + +@media only screen and (min-width: 992px) and (max-width: 1199px) { + +} + +@media only screen and (min-width: 1200px) and (max-width: 1919px) { + +} + +@media only screen and (min-width: 1920px) { + +} \ No newline at end of file diff --git a/custom/public/img/develop.svg b/custom/public/img/develop.svg new file mode 100644 index 000000000..fb8412dda --- /dev/null +++ b/custom/public/img/develop.svg @@ -0,0 +1,711 @@ + + + diff --git a/custom/public/img/gitopeni-index-01.svg b/custom/public/img/gitopeni-index-01.svg new file mode 100644 index 000000000..18401747f --- /dev/null +++ b/custom/public/img/gitopeni-index-01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/gitopeni-index-02.svg b/custom/public/img/gitopeni-index-02.svg new file mode 100644 index 000000000..4b47fe632 --- /dev/null +++ b/custom/public/img/gitopeni-index-02.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/i-code-pic.jpg b/custom/public/img/i-code-pic.jpg new file mode 100644 index 000000000..cba627b73 Binary files /dev/null and b/custom/public/img/i-code-pic.jpg differ diff --git a/custom/public/img/i-data-pic.jpg b/custom/public/img/i-data-pic.jpg new file mode 100644 index 000000000..2e5dbfb1f Binary files /dev/null and b/custom/public/img/i-data-pic.jpg differ diff --git a/custom/public/img/i-pic-01.svg b/custom/public/img/i-pic-01.svg new file mode 100644 index 000000000..97d8e4165 --- /dev/null +++ b/custom/public/img/i-pic-01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/i-pic-02.svg b/custom/public/img/i-pic-02.svg new file mode 100644 index 000000000..ef8606b5e --- /dev/null +++ b/custom/public/img/i-pic-02.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/i-pic-03.svg b/custom/public/img/i-pic-03.svg new file mode 100644 index 000000000..779a28d5f --- /dev/null +++ b/custom/public/img/i-pic-03.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/i-pic-04.svg b/custom/public/img/i-pic-04.svg new file mode 100644 index 000000000..96bb3e455 --- /dev/null +++ b/custom/public/img/i-pic-04.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/i-yunnao.svg b/custom/public/img/i-yunnao.svg new file mode 100644 index 000000000..aeb1e9435 --- /dev/null +++ b/custom/public/img/i-yunnao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/icon-403@2x.png b/custom/public/img/icon-403@2x.png new file mode 100644 index 000000000..65bc29cd8 Binary files /dev/null and b/custom/public/img/icon-403@2x.png differ diff --git a/custom/public/img/icon-404@2x.png b/custom/public/img/icon-404@2x.png new file mode 100644 index 000000000..d5e606cbb Binary files /dev/null and b/custom/public/img/icon-404@2x.png differ diff --git a/custom/public/img/icon-500@2x.png b/custom/public/img/icon-500@2x.png new file mode 100644 index 000000000..e493b71c7 Binary files /dev/null and b/custom/public/img/icon-500@2x.png differ diff --git a/custom/public/img/logo-w.svg b/custom/public/img/logo-w.svg new file mode 100644 index 000000000..867acc1e2 --- /dev/null +++ b/custom/public/img/logo-w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/logo.svg b/custom/public/img/logo.svg new file mode 100644 index 000000000..b2330f885 --- /dev/null +++ b/custom/public/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/models/cloudbrain.go b/models/cloudbrain.go index c8000c5a6..f9c0fb4e0 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -2,7 +2,6 @@ package models import ( "encoding/json" - "errors" "fmt" "time" "xorm.io/xorm" @@ -300,7 +299,7 @@ type CommitImageResult struct { Payload map[string]interface{} `json:"payload"` } -type StopJobResult struct { +type CloudBrainResult struct { Code string `json:"code"` Msg string `json:"msg"` } @@ -569,7 +568,7 @@ func getRepoCloudBrain(cb *Cloudbrain) (*Cloudbrain, error) { if err != nil { return nil, err } else if !has { - return nil, errors.New("cloudbrain task is not found") + return nil, ErrJobNotExist{} } return cb, nil } @@ -609,3 +608,8 @@ func deleteJob(e Engine, job *Cloudbrain) error { _, err := e.ID(job.ID).Delete(job) return err } + +func GetCloudbrainByName(jobName string) (*Cloudbrain, error) { + cb := &Cloudbrain{JobName: jobName} + return getRepoCloudBrain(cb) +} diff --git a/models/error.go b/models/error.go index 66bb74de0..9d1c68658 100755 --- a/models/error.go +++ b/models/error.go @@ -1987,3 +1987,15 @@ func IsErrFileChunkNotExist(err error) bool { _, ok := err.(ErrFileChunkNotExist) return ok } + +type ErrJobNotExist struct { +} + +func IsErrJobNotExist(err error) bool { + _, ok := err.(ErrJobNotExist) + return ok +} + +func (err ErrJobNotExist) Error() string { + return fmt.Sprintf("the job does not exist") +} diff --git a/modules/cloudbrain/resty.go b/modules/cloudbrain/resty.go index ed6157776..fa9b25011 100755 --- a/modules/cloudbrain/resty.go +++ b/modules/cloudbrain/resty.go @@ -2,6 +2,7 @@ package cloudbrain import ( "code.gitea.io/gitea/modules/log" + "encoding/json" "fmt" "code.gitea.io/gitea/models" @@ -143,14 +144,26 @@ sendjob: return nil, fmt.Errorf("resty GetImages: %v", err) } - if getImagesResult.Code == "S401" && retry < 1 { + var response models.CloudBrainResult + err = json.Unmarshal(res.Body(), &response) + if err != nil { + log.Error("json.Unmarshal failed: %s", err.Error()) + return &getImagesResult, fmt.Errorf("json.Unmarshal failed: %s", err.Error()) + } + + if response.Code == "S401" && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob } + if len(response.Code) != 0 { + log.Error("getImagesResult failed(%s): %s", response.Code, response.Msg) + return &getImagesResult, fmt.Errorf("getImagesResult failed(%s): %s", response.Code, response.Msg) + } + if getImagesResult.Code != Success { - return &getImagesResult, fmt.Errorf("getImgesResult err: %s", res.String()) + return &getImagesResult, fmt.Errorf("getImagesResult err: %s", res.String()) } return &getImagesResult, nil @@ -223,7 +236,7 @@ sendjob: func StopJob(jobID string) error { checkSetting() client := getRestyClient() - var result models.StopJobResult + var result models.CloudBrainResult retry := 0 diff --git a/modules/git/repo.go b/modules/git/repo.go index 644ff0928..a2a4d28af 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -434,3 +434,38 @@ func GetDivergingCommits(repoPath string, baseBranch string, targetBranch string return DivergeObject{ahead, behind}, nil } + +type Contributor struct { + CommitCnt int + Committer string + Email string +} + +func GetContributors(repoPath string) ([]Contributor, error){ + cmd := NewCommand("shortlog", "-sne", "--all") + stdout, err := cmd.RunInDir(repoPath) + if err != nil { + return nil, err + } + stdout = strings.Trim(stdout, "\n") + contributorRows := strings.Split(stdout, "\n") + if len(contributorRows) > 0 { + contributorsInfo := make([]Contributor, len(contributorRows)) + for i := 0; i < len(contributorRows); i++ { + var oneCount string = strings.Trim(contributorRows[i], " ") + if strings.Index(oneCount, "\t") < 0 { + continue + } + number := oneCount[0:strings.Index(oneCount, "\t")] + commitCnt, _ := strconv.Atoi(number) + committer := oneCount[strings.Index(oneCount, "\t")+1:strings.LastIndex(oneCount, " ")] + committer = strings.Trim(committer, " ") + email := oneCount[strings.Index(oneCount, "<")+1:strings.Index(oneCount, ">")] + contributorsInfo[i] = Contributor{ + commitCnt, committer, email, + } + } + return contributorsInfo, nil + } + return nil, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c4956c65f..e809a33e8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1,4 +1,5 @@ home = Home +index = Home dashboard = Dashboard explore = Explore datasets = Datasets @@ -86,6 +87,8 @@ write = Write preview = Preview loading = Loading… +error404_index = Request forbidden by administrative rules +error500_index = Internal Server Error error404 = The page you are trying to reach either does not exist or you are not authorized to view it. [error] @@ -702,6 +705,7 @@ delete= delete owner = Owner repo_name = Repository Name repo_name_helper = Good repository names use short, memorable and unique keywords. +repo_owner_helper = Some organizations may not show up in the dropdown due to a maximum repository count limit. repo_size = Repository Size template = Template template_select = Select a template. @@ -1817,6 +1821,7 @@ org_full_name_holder = Organization Full Name org_name_helper = Organization names should be short and memorable. create_org = Create Organization repo_updated = Updated +home = Home people = People teams = Teams lower_members = members @@ -2482,9 +2487,9 @@ error.unit_not_allowed = You are not allowed to access this repository section. [custom] head.community = Community -head.project = Project +head.project = Repositories head.openi = OpenI -head.dataset = Dataset +head.dataset = Datasets foot.council = Council foot.technical_committee = Technical Committee foot.join = Join OpenI diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 8c0509752..9747bc7dc 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1,5 +1,5 @@ home=个人中心 -index=首页 +index=个人中心 dashboard=个人中心 explore=探索 datasets=数据集 @@ -87,6 +87,8 @@ write=撰写 preview=预览 loading=正在加载... +error404_index = 您的访问受限! +error500_index = 抱歉!您指定的网页无法访问。 error404=您正尝试访问的页面 不存在 或 您尚未被授权 查看该页面。 [error] @@ -287,7 +289,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=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。 @@ -704,6 +706,7 @@ delete=删除 owner=拥有者 repo_name=项目名称 repo_name_helper=好的存储库名称使用简短、深刻和独特的关键字。 +repo_owner_helper = 由于最大存储库数量限制,某些组织名称可能不会显示在下拉列表中。 repo_size=项目大小 template=模板 template_select=选择模板 @@ -1819,6 +1822,7 @@ org_full_name_holder=组织全名 org_name_helper=组织名字应该简单明了。 create_org=创建组织 repo_updated=最后更新于 +home=组织主页 people=组织成员 teams=组织团队 lower_members=名成员 @@ -2482,7 +2486,7 @@ error.unit_not_allowed=您没有权限访问此项目单元 [custom] head.community=启智社区 -head.project=所有项目 +head.project=项目 head.openi=启智项目 head.dataset=数据集 foot.council=理事会 diff --git a/routers/org/home.go b/routers/org/home.go old mode 100644 new mode 100755 index fa61218d3..b11c179c1 --- a/routers/org/home.go +++ b/routers/org/home.go @@ -20,6 +20,7 @@ const ( // Home show organization home page func Home(ctx *context.Context) { ctx.SetParams(":org", ctx.Params(":username")) + ctx.Data["PageIsOrgHome"] = true context.HandleOrgAssignment(ctx) if ctx.Written() { return diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index 27642d7bb..1088f47d3 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -168,11 +168,24 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form) return } + + _, err := models.GetCloudbrainByName(jobName) + if err == nil { + log.Error("the job name did already exist", ctx.Data["MsgID"]) + ctx.RenderWithErr("the job name did already exist", tplCloudBrainNew, &form) + return + } else { + if !models.IsErrJobNotExist(err) { + log.Error("system error, %v", err, ctx.Data["MsgID"]) + ctx.RenderWithErr("system error", tplCloudBrainNew, &form) + return + } + } repo := ctx.Repo.Repository downloadCode(repo, codePath) modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath - err := os.MkdirAll(modelPath, os.ModePerm) + err = os.MkdirAll(modelPath, os.ModePerm) if err != nil { ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) return diff --git a/routers/repo/view.go b/routers/repo/view.go index 9c9cdc06b..76593ecc7 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -7,6 +7,7 @@ package repo import ( "bytes" + "code.gitea.io/gitea/modules/options" "encoding/base64" "fmt" gotemplate "html/template" @@ -567,9 +568,32 @@ func safeURL(address string) string { return u.String() } +type ContributorInfo struct { + UserInfo *models.User + Email string // for contributor who is not a registered user +} + // Home render repository home page func Home(ctx *context.Context) { if len(ctx.Repo.Units) > 0 { + //get repo contributors info + contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath()) + if err == nil && contributors != nil { + var contributorInfos []*ContributorInfo + for _, c := range contributors { + user, err := models.GetUserByEmail(c.Email) + if err == nil { + contributorInfos = append(contributorInfos, &ContributorInfo{ + user, c.Email, + }) + } else { + contributorInfos = append(contributorInfos, &ContributorInfo{ + nil, c.Email, + }) + } + } + ctx.Data["ContributorInfo"] = contributorInfos + } if ctx.Repo.Repository.IsBeingCreated() { task, err := models.GetMigratingTask(ctx.Repo.Repository.ID) if err != nil { @@ -611,6 +635,39 @@ func Home(ctx *context.Context) { ctx.NotFound("Home", fmt.Errorf(ctx.Tr("units.error.no_unit_allowed_repo"))) } +func renderLicense(ctx *context.Context) { + entry, err := ctx.Repo.Commit.GetTreeEntryByPath("LICENSE") + if err != nil { + log.Error(err.Error()) + return + } + blob := entry.Blob() + dataRc, err := blob.DataAsync() + if err != nil { + log.Error("DataAsync", err) + return + } + defer dataRc.Close() + buf, err := ioutil.ReadAll(dataRc) + if err != nil { + log.Error("DataAsync", err) + return + } + for _, f := range models.Licenses { + license, err := options.License(f) + if err != nil { + log.Error("failed to get license content: %v, err:%v", f, err) + continue + } + if bytes.Compare(buf,license) == 0 { + log.Info("got matched license:%v",f) + ctx.Data["LICENSE"] = f + return + } + } + log.Info("not found matched license,repo:%v",ctx.Repo.Repository.Name) +} + func renderLanguageStats(ctx *context.Context) { langs, err := ctx.Repo.Repository.GetTopLanguageStats(5) if err != nil { @@ -657,6 +714,8 @@ func renderCode(ctx *context.Context) { // Get Topics of this repo renderRepoTopics(ctx) + // Get license of this repo + renderLicense(ctx) if ctx.Written() { return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 4f1f6f314..100ff009f 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -6,6 +6,7 @@ package routes import ( "bytes" + "code.gitea.io/gitea/routers/secure" "encoding/gob" "net/http" "path" @@ -21,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/options" "code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/routers" @@ -265,7 +267,7 @@ func RegisterRoutes(m *macaron.Macaron) { ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) - reqBasicAuth := context.Toggle(&context.ToggleOptions{BasicAuthRequired: true}) + reqBasicAuth := context.Toggle(&context.ToggleOptions{BasicAuthRequired: true, DisableCSRF: true}) bindIgnErr := binding.BindIgnErr validation.AddBindingRules() @@ -305,7 +307,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Head("/", func() string { return "" }) - m.Get("/", routers.ExploreRepos) + m.Get("/", routers.Dashboard) m.Get("/dashboard", routers.Dashboard) m.Group("/explore", func() { m.Get("", func(ctx *context.Context) { @@ -1125,6 +1127,11 @@ func RegisterRoutes(m *macaron.Macaron) { apiv1.RegisterRoutes(m) }, handlers...) + //secure api, + m.Group("/secure", func() { + m.Post("/user", binding.Bind(structs.CreateUserOption{}), secure.CreateUser) + }, reqBasicAuth) + m.Group("/api/internal", func() { // package name internal is ideal but Golang is not allowed, so we use private as package name. private.RegisterRoutes(m) diff --git a/routers/secure/user.go b/routers/secure/user.go new file mode 100755 index 000000000..8c06b0dab --- /dev/null +++ b/routers/secure/user.go @@ -0,0 +1,114 @@ +// Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2019 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 secure + +import ( + "net/http" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/password" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/mailer" +) + +func parseLoginSource(ctx *context.Context, u *models.User, sourceID int64, loginName string) { + if sourceID == 0 { + return + } + + source, err := models.GetLoginSourceByID(sourceID) + if err != nil { + if models.IsErrLoginSourceNotExist(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err.Error()) + } else { + ctx.Error(http.StatusInternalServerError, "GetLoginSourceByID", err.Error()) + } + return + } + + u.LoginType = source.Type + u.LoginSource = source.ID + u.LoginName = loginName +} + +// CreateUser create a user +func CreateUser(ctx *context.Context, form api.CreateUserOption) { + // swagger:operation POST /admin/users admin adminCreateUser + // --- + // summary: Create a user + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateUserOption" + // responses: + // "201": + // "$ref": "#/responses/User" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + // "422": + // "$ref": "#/responses/validationError" + + u := &models.User{ + Name: form.Username, + FullName: form.FullName, + Email: form.Email, + Passwd: form.Password, + MustChangePassword: false, + IsActive: !setting.Service.RegisterEmailConfirm, + LoginType: models.LoginPlain, + } + if form.MustChangePassword != nil { + u.MustChangePassword = *form.MustChangePassword + } + + parseLoginSource(ctx, u, form.SourceID, form.LoginName) + if ctx.Written() { + return + } + if !password.IsComplexEnough(form.Password) { + log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) + ctx.JSON(http.StatusBadRequest, map[string]string{ + "error_msg": "PasswordComplexity", + }) + return + } + if err := models.CreateUser(u); err != nil { + if models.IsErrUserAlreadyExist(err) || + models.IsErrEmailAlreadyUsed(err) || + models.IsErrNameReserved(err) || + models.IsErrNameCharsNotAllowed(err) || + models.IsErrNamePatternNotAllowed(err) { + log.Error("CreateUser failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ + "error_msg": err.Error(), + }) + } else { + log.Error("CreateUser failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.JSON(http.StatusInternalServerError, map[string]string{ + "error_msg": err.Error(), + }) + } + return + } + log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) + + // Send email notification. + if form.SendNotify { + mailer.SendRegisterNotifyMail(ctx.Locale, u) + } + ctx.JSON(http.StatusCreated, convert.ToUser(u, ctx.IsSigned, ctx.User.IsAdmin)) +} diff --git a/routers/user/auth.go b/routers/user/auth.go index 421598347..dc5c5536b 100755 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -535,14 +535,23 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR log.Error("Unable to store session: %v", err) } - // Language setting of the user overwrites the one previously set // If the user does not have a locale set, we save the current one. if len(u.Language) == 0 { - u.Language = ctx.Locale.Language() + if len(ctx.GetCookie("lang")) != 0 { + u.Language = ctx.GetCookie("lang") + } else { + u.Language = ctx.Locale.Language() + } + if err := models.UpdateUserCols(u, "language"); err != nil { log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language)) return setting.AppSubURL + "/" } + } else { + // Language setting of the user use the one previously set + if len(ctx.GetCookie("lang")) != 0 { + u.Language = ctx.GetCookie("lang") + } } ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 921fe7f8b..86ef0d98e 100755 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -5,15 +5,15 @@