Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/1478tags/v1.22.1.4
| @@ -64,6 +64,11 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
| ctx.Redirect(setting.AppSubURL + "/") | |||
| return | |||
| } | |||
| if ctx.QueryBool("course") { | |||
| ctx.Redirect(setting.AppSubURL + "/" + setting.Course.OrgName) | |||
| return | |||
| } | |||
| } | |||
| // Redirect to dashboard if user tries to visit any non-login page. | |||
| @@ -45,8 +45,8 @@ type Context struct { | |||
| IsSigned bool | |||
| IsBasicAuth bool | |||
| Repo *Repository | |||
| Org *Organization | |||
| Repo *Repository | |||
| Org *Organization | |||
| Cloudbrain *models.Cloudbrain | |||
| } | |||
| @@ -347,9 +347,9 @@ func Contexter() macaron.Handler { | |||
| ctx.Data["EnableSwagger"] = setting.API.EnableSwagger | |||
| ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn | |||
| notice, _ := notice.GetNewestNotice() | |||
| if notice != nil { | |||
| ctx.Data["notice"] = *notice | |||
| notices, _ := notice.GetNewestNotice() | |||
| if notices != nil { | |||
| ctx.Data["notices"] = notices | |||
| } | |||
| c.Map(ctx) | |||
| } | |||
| @@ -16,27 +16,31 @@ const ( | |||
| ) | |||
| type Notice struct { | |||
| Title string | |||
| Link string | |||
| Visible int //0 invisible, 1 visible | |||
| Title string | |||
| Link string | |||
| Visible int //0 invisible, 1 visible | |||
| } | |||
| type NoticeResponse struct { | |||
| Notices []*Notice | |||
| CommitId string | |||
| } | |||
| var lock int32 = 0 | |||
| func GetNewestNotice() (*Notice, error) { | |||
| func GetNewestNotice() (*NoticeResponse, error) { | |||
| defer func() { | |||
| if err := recover(); err != nil { | |||
| log.Error("recover error", err) | |||
| } | |||
| }() | |||
| var notice *Notice | |||
| var notice *NoticeResponse | |||
| var err error | |||
| if setting.CacheOn { | |||
| notice, err = getNewestNoticeFromCacheAndDisk() | |||
| notice, err = getNewestNoticesFromCacheAndDisk() | |||
| } else { | |||
| notice, err = getNewestNoticeFromDisk() | |||
| notice, err = getNewestNoticesFromDisk() | |||
| } | |||
| if err != nil { | |||
| @@ -49,34 +53,39 @@ func getNoticeTimeout() time.Duration { | |||
| return time.Duration(setting.CacheTimeOutSecond) * time.Second | |||
| } | |||
| func getNewestNoticeFromDisk() (*Notice, error) { | |||
| func getNewestNoticesFromDisk() (*NoticeResponse, error) { | |||
| log.Debug("Get notice from disk") | |||
| repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfNoticeRepo, setting.RepoNameOfNoticeRepo, setting.RefNameOfNoticeRepo, setting.TreePathOfNoticeRepo) | |||
| repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfNoticeRepo, setting.RepoNameOfNoticeRepo) | |||
| if err != nil { | |||
| log.Error("get notice repo failed, error=%v", err) | |||
| return nil, err | |||
| } | |||
| repoFile, err := models.ReadLatestFileInRepo(repo.OwnerName, repo.Name, setting.RefNameOfNoticeRepo, setting.TreePathOfNoticeRepo) | |||
| if err != nil { | |||
| log.Error("GetNewestNotice failed, error=%v", err) | |||
| return nil, err | |||
| } | |||
| notice := &Notice{} | |||
| json.Unmarshal(repoFile.Content, notice) | |||
| if notice.Title == "" { | |||
| res := &NoticeResponse{} | |||
| json.Unmarshal(repoFile.Content, res) | |||
| if res == nil || len(res.Notices) == 0 { | |||
| return nil, err | |||
| } | |||
| notice.CommitId = repoFile.CommitId | |||
| return notice, nil | |||
| res.CommitId = repoFile.CommitId | |||
| return res, nil | |||
| } | |||
| func getNewestNoticeFromCacheAndDisk() (*Notice, error) { | |||
| func getNewestNoticesFromCacheAndDisk() (*NoticeResponse, error) { | |||
| v, success := noticeCache.Get(NOTICE_CACHE_KEY) | |||
| if success { | |||
| log.Debug("Get notice from cache,value = %v", v) | |||
| if v == nil { | |||
| return nil, nil | |||
| } | |||
| n := v.(*Notice) | |||
| n := v.(*NoticeResponse) | |||
| return n, nil | |||
| } | |||
| notice, err := getNewestNoticeFromDisk() | |||
| notice, err := getNewestNoticesFromDisk() | |||
| if err != nil { | |||
| log.Error("GetNewestNotice failed, error=%v", err) | |||
| noticeCache.Set(NOTICE_CACHE_KEY, nil, 30*time.Second) | |||
| @@ -116,8 +116,16 @@ func checkAutoLogin(ctx *context.Context) bool { | |||
| } | |||
| if isSucceed { | |||
| isCourse := ctx.QueryBool("course") | |||
| ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true) | |||
| ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL)) | |||
| if redirectTo == "" && isCourse { | |||
| redirectToCourse := setting.AppSubURL + "/" + setting.Course.OrgName | |||
| ctx.RedirectToFirst(redirectToCourse) | |||
| } else { | |||
| ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL)) | |||
| } | |||
| return true | |||
| } | |||
| @@ -143,6 +151,7 @@ func SignIn(ctx *context.Context) { | |||
| ctx.Data["Title"] = ctx.Tr("sign_in") | |||
| ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | |||
| ctx.Data["PageIsSignIn"] = true | |||
| ctx.Data["IsCourse"] = ctx.QueryBool("course") | |||
| ctx.Data["PageIsLogin"] = true | |||
| ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | |||
| ctx.Data["EnableCloudBrain"] = true | |||
| @@ -182,6 +191,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||
| ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | |||
| ctx.Data["PageIsSignIn"] = true | |||
| ctx.Data["PageIsLogin"] = true | |||
| ctx.Data["IsCourse"] = ctx.QueryBool("course") | |||
| ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | |||
| if ctx.HasError() { | |||
| @@ -565,6 +575,12 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR | |||
| return setting.AppSubURL + "/dashboard" | |||
| } | |||
| isCourse := ctx.QueryBool("course") | |||
| if isCourse { | |||
| redirectToCourse := setting.AppSubURL + "/" + setting.Course.OrgName | |||
| ctx.RedirectToFirst(redirectToCourse) | |||
| return redirectToCourse | |||
| } | |||
| if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { | |||
| ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true) | |||
| if obeyRedirect { | |||
| @@ -200,16 +200,7 @@ var _hmt = _hmt || []; | |||
| <div class="ui top secondary stackable main menu following bar dark"> | |||
| {{template "base/head_navbar" .}} | |||
| </div><!-- end bar --> | |||
| <!-- {{if not .IsCourse}} --> | |||
| <div class="notic_content" id ="notic_content" style="display: none;"> | |||
| <a href={{.notice.Link}} class="a_width"> | |||
| <marquee behavior="scroll" direction="left"> | |||
| {{.notice.Title}} | |||
| </marquee> | |||
| </a> | |||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
| </div> | |||
| <!-- {{end}} --> | |||
| {{template "base/head_notice" .}} | |||
| {{end}} | |||
| {{/* | |||
| </div> | |||
| @@ -233,7 +224,15 @@ var _hmt = _hmt || []; | |||
| }else{ | |||
| isNewNotice=false; | |||
| } | |||
| if (JSON.parse("{{.notice.Visible}}")){ | |||
| let isShowNoticeTag = false; | |||
| let notices= {{.notices.Notices}} | |||
| for (i =0;i<notices.length;i++){ | |||
| if (notices[i].Visible==1){ | |||
| isShowNoticeTag =true; | |||
| break; | |||
| } | |||
| } | |||
| if (isShowNoticeTag){ | |||
| if(isNewNotice){ | |||
| document.getElementById("notic_content").style.display='block' | |||
| }else{ | |||
| @@ -249,6 +248,7 @@ var _hmt = _hmt || []; | |||
| document.getElementById("notic_content").style.display='none' | |||
| } | |||
| } | |||
| if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
| isShowNotice(); | |||
| } | |||
| @@ -201,14 +201,7 @@ var _hmt = _hmt || []; | |||
| <div class="ui top secondary stackable main menu following bar dark"> | |||
| {{template "base/head_navbar_fluid" .}} | |||
| </div><!-- end bar --> | |||
| <div class="notic_content" id ="notic_content" style="display: none;"> | |||
| <a href={{.notice.Link}} class="a_width"> | |||
| <marquee behavior="scroll" direction="left"> | |||
| {{.notice.Title}} | |||
| </marquee> | |||
| </a> | |||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
| </div> | |||
| {{template "base/head_notice" .}} | |||
| {{end}} | |||
| {{/* | |||
| </div> | |||
| @@ -232,7 +225,15 @@ var _hmt = _hmt || []; | |||
| }else{ | |||
| isNewNotice=false; | |||
| } | |||
| if (JSON.parse("{{.notice.Visible}}")){ | |||
| let isShowNoticeTag = false; | |||
| let notices= {{.notices.Notices}} | |||
| for (i =0;i<notices.length;i++){ | |||
| if (notices[i].Visible==1){ | |||
| isShowNoticeTag =true; | |||
| break; | |||
| } | |||
| } | |||
| if (isShowNoticeTag){ | |||
| if(isNewNotice){ | |||
| document.getElementById("notic_content").style.display='block' | |||
| }else{ | |||
| @@ -248,6 +249,7 @@ var _hmt = _hmt || []; | |||
| document.getElementById("notic_content").style.display='none' | |||
| } | |||
| } | |||
| if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
| isShowNotice(); | |||
| } | |||
| @@ -205,14 +205,7 @@ var _hmt = _hmt || []; | |||
| <div class="ui top secondary stackable main menu following bar dark"> | |||
| {{template "base/head_navbar" .}} | |||
| </div><!-- end bar --> | |||
| <div class="notic_content" id ="notic_content" style="display: none;" > | |||
| <a href={{.notice.Link}} class="a_width"> | |||
| <marquee behavior="scroll" direction="left"> | |||
| {{.notice.Title}} | |||
| </marquee> | |||
| </a> | |||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
| </div> | |||
| {{template "base/head_notice" .}} | |||
| {{end}} | |||
| {{/* | |||
| </div> | |||
| @@ -236,7 +229,15 @@ var _hmt = _hmt || []; | |||
| }else{ | |||
| isNewNotice=false; | |||
| } | |||
| if (JSON.parse("{{.notice.Visible}}")){ | |||
| let isShowNoticeTag = false; | |||
| let notices= {{.notices.Notices}} | |||
| for (i =0;i<notices.length;i++){ | |||
| if (notices[i].Visible==1){ | |||
| isShowNoticeTag =true; | |||
| break; | |||
| } | |||
| } | |||
| if (isShowNoticeTag){ | |||
| if(isNewNotice){ | |||
| document.getElementById("notic_content").style.display='block' | |||
| }else{ | |||
| @@ -252,6 +253,7 @@ var _hmt = _hmt || []; | |||
| document.getElementById("notic_content").style.display='none' | |||
| } | |||
| } | |||
| if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
| isShowNotice(); | |||
| } | |||
| @@ -211,13 +211,25 @@ | |||
| </div> | |||
| </form> | |||
| {{if .ShowRegistrationButton}} | |||
| <a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
| {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
| {{if .IsCourse}} | |||
| <a class="item{{if .PageIsSignUp}} active{{end}}" href="https://git.openi.org.cn/user/sign_up" target="_blank"> | |||
| {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
| {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
| </a> | |||
| {{end}} | |||
| {{end}} | |||
| {{if .IsCourse}} | |||
| <a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="https://git.openi.org.cn/user/login?course=true" target="_blank"> | |||
| {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
| {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
| </a> | |||
| {{end}} | |||
| <a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
| {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
| </a> | |||
| </div><!-- end anonymous right menu --> | |||
| {{end}} | |||
| @@ -0,0 +1,32 @@ | |||
| {{if not .IsCourse}} | |||
| {{ if .notices}} | |||
| <div class="notic_content" id ="notic_content" style="display: block; position: relative"> | |||
| <diV class="ui container"> | |||
| <marquee behavior="scroll" direction="left"> | |||
| {{ $firstTag := true }} | |||
| {{range .notices.Notices}} | |||
| {{if eq .Visible 1}} | |||
| {{if $firstTag}} | |||
| <a href={{.Link}} class="a_width" style = 'margin-left: 0px !important;' target="_blank"> | |||
| <i class="ri-arrow-right-s-line"></i> | |||
| {{.Title}} | |||
| </a> | |||
| {{else}} | |||
| <a href={{.Link}} class="a_width" target="_blank"> | |||
| <i class="ri-arrow-right-s-line"></i> | |||
| {{.Title}} | |||
| </a> | |||
| {{end}} | |||
| {{ $firstTag = false }} | |||
| {{end}} | |||
| {{end}} | |||
| </marquee> | |||
| <div class="item right" style="position:absolute;right: 1px;top:0px;"> | |||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
| </div> | |||
| </diV> | |||
| </div> | |||
| {{end}} | |||
| {{end}} | |||
| @@ -201,14 +201,7 @@ var _hmt = _hmt || []; | |||
| <div class="ui top secondary stackable main menu following bar dark"> | |||
| {{template "base/head_navbar_pro" .}} | |||
| </div><!-- end bar --> | |||
| <div class="notic_content" id ="notic_content" style="display: none;" > | |||
| <a href={{.notice.Link}} class="a_width"> | |||
| <marquee behavior="scroll" direction="left"> | |||
| {{.notice.Title}} | |||
| </marquee> | |||
| </a> | |||
| <i class="icon icon-octicon x_icon" onclick="closeNoice()">{{svg "octicon-x" 16}}</i> | |||
| </div> | |||
| {{template "base/head_notice" .}} | |||
| {{end}} | |||
| {{/* | |||
| </div> | |||
| @@ -233,7 +226,15 @@ var _hmt = _hmt || []; | |||
| }else{ | |||
| isNewNotice=false; | |||
| } | |||
| if (JSON.parse("{{.notice.Visible}}")){ | |||
| let isShowNoticeTag = false; | |||
| let notices= {{.notices.Notices}} | |||
| for (i =0;i<notices.length;i++){ | |||
| if (notices[i].Visible==1){ | |||
| isShowNoticeTag =true; | |||
| break; | |||
| } | |||
| } | |||
| if (isShowNoticeTag){ | |||
| if(isNewNotice){ | |||
| document.getElementById("notic_content").style.display='block' | |||
| }else{ | |||
| @@ -249,6 +250,7 @@ var _hmt = _hmt || []; | |||
| document.getElementById("notic_content").style.display='none' | |||
| } | |||
| } | |||
| if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
| isShowNotice(); | |||
| } | |||
| @@ -96,7 +96,7 @@ | |||
| {{if .Topics }} | |||
| <div class="omit tags " style="position: relative;"> | |||
| {{range .Topics}} | |||
| {{if ne . "" }}<a style="max-width:100%;margin: 5px 0;display:inline-flex;" ><span class="ui small label topic course_topic" >{{.}}</span></a>{{end}} | |||
| {{if ne . "" }}<a style="max-width:100%;margin: 5px 0;display:inline-flex;cursor:default" ><span class="ui small label topic course_topic" >{{.}}</span></a>{{end}} | |||
| {{end}} | |||
| </div> | |||
| @@ -136,11 +136,7 @@ | |||
| <div class="ui sixteen wide mobile six wide tablet four wide computer column"> | |||
| <div class=" ui bottom attached segment text center noborder text center" > | |||
| {{if .IsSigned}} | |||
| <a style="width: 80%;" class="ui green button bpadding" href="{{AppSubUrl}}/course/create"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> {{.i18n.Tr "org.release_course"}} </a> | |||
| {{else}} | |||
| <a style="width: 80%;" class="ui green button bpadding" href="{{AppSubUrl}}/user/login"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> {{.i18n.Tr "org.release_course"}} </a> | |||
| {{end}} | |||
| <a style="width: 80%;" class="ui green button bpadding" href="https://git.openi.org.cn/course/create" target="_blank"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> {{.i18n.Tr "org.release_course"}} </a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -209,7 +205,7 @@ | |||
| {{if .IsSigned}} | |||
| <a class="ui blue basic button" onclick="jion_course_team()" style="width: 80%;"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
| {{else}} | |||
| <a class="ui blue basic button" href="{{AppSubUrl}}/user/login" style="width: 80%;"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
| <a class="ui blue basic button" href="https://git.openi.org.cn/user/login?course=true" style="width: 80%;" target="_blank"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| @@ -29,7 +29,11 @@ | |||
| <div class="ui grid"> | |||
| <div class="column"> | |||
| <form class="ui form" action="{{.SignInLink}}" method="post"> | |||
| {{if .IsCourse}} | |||
| <form class="ui form" action="{{.SignInLink}}?course=true" method="post"> | |||
| {{else}} | |||
| <form class="ui form" action="{{.SignInLink}}" method="post"> | |||
| {{end}} | |||
| {{.CsrfTokenHtml}} | |||
| <div class="field"> | |||
| <div class="ui left icon input {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | |||
| @@ -604,10 +604,13 @@ display: block; | |||
| font-weight: bold !important; | |||
| } | |||
| .a_width{ | |||
| width: 50% !important; | |||
| display:inline-block !important; | |||
| // width: 50% !important; | |||
| margin-left: 20em; | |||
| // display:inline-block !important; | |||
| } | |||
| .a_width i{ | |||
| vertical-align:middle!important; | |||
| } | |||
| .footer_icon{ | |||
| display: inline-block !important; | |||
| vertical-align: middle !important; | |||