Browse Source

Merge branch 'V20211228' into fix-1095

tags/v1.21.12.2^2
zhoupzh 3 years ago
parent
commit
f4a6767bd1
14 changed files with 636 additions and 14 deletions
  1. +13
    -0
      models/error.go
  2. +2
    -0
      models/models.go
  3. +165
    -0
      models/repo_tag.go
  4. +4
    -0
      modules/auth/org.go
  5. +3
    -0
      options/locale/locale_en-US.ini
  6. +3
    -0
      options/locale/locale_zh-CN.ini
  7. +8
    -0
      routers/org/home.go
  8. +98
    -0
      routers/org/tag.go
  9. +4
    -0
      routers/routes/routes.go
  10. +5
    -4
      templates/org/home.tmpl
  11. +3
    -2
      templates/org/member/members.tmpl
  12. +37
    -5
      templates/org/navber.tmpl
  13. +288
    -0
      templates/org/select_pro.tmpl
  14. +3
    -3
      templates/org/team/teams.tmpl

+ 13
- 0
models/error.go View File

@@ -1999,3 +1999,16 @@ func IsErrJobNotExist(err error) bool {
func (err ErrJobNotExist) Error() string {
return fmt.Sprintf("the job does not exist")
}

type ErrTagNotExist struct {
TagID int64
}

func (err ErrTagNotExist) Error() string {
return fmt.Sprintf("the tag does not exist")
}

func IsErrTagNotExist(err error) bool {
_, ok := err.(ErrTagNotExist)
return ok
}

+ 2
- 0
models/models.go View File

@@ -134,6 +134,8 @@ func init() {
new(BlockChain),
new(RecommendOrg),
new(AiModelManage),
new(OfficialTag),
new(OfficialTagRepos),
)

tablesStatistic = append(tablesStatistic,


+ 165
- 0
models/repo_tag.go View File

@@ -0,0 +1,165 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"fmt"
)

const DefaultOrgTagLimit = -1

type OfficialTag struct {
ID int64 `xorm:"pk autoincr"`
Name string `xorm:"NOT NULL"`
Code string `xorm:"NOT NULL"`
Limit int `xorm:"NOT NULL default(-1)"`
Status int `xorm:"NOT NULL default(0)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

type OfficialTagRepos struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"NOT NULL INDEX"`
TagID int64 `xorm:"NOT NULL"`
RepoID int64 `xorm:"NOT NULL INDEX"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

type TagReposBrief struct {
RepoID int64
RepoName string
TagID int64
}

type TagReposSelected struct {
RepoID int64
RepoName string
Selected bool
}

type TagsDetail struct {
TagId int64
TagName string
TagLimit int
RepoList []Repository
}

func GetTagByID(id int64) (*OfficialTag, error) {
r := &OfficialTag{
ID: id,
}
has, err := x.Get(r)
if err != nil {
return nil, err
} else if !has {
return nil, ErrTagNotExist{0}
}
return r, nil
}

func UpdateTagReposByID(tagID, orgID int64, repoIdList []int64) error {
sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return fmt.Errorf("UpdateTagReposByID[tagId: %d, orgID: %d,error:%v", tagID, orgID, err)
}
//delete old tag repos
r := &OfficialTagRepos{
TagID: tagID,
OrgID: orgID,
}
_, err := sess.Delete(r)
if err != nil {
return err
}

if len(repoIdList) == 0 {
return sess.Commit()
}

//add new tag repos
data := make([]*OfficialTagRepos, 0)
for _, repoId := range repoIdList {
data = append(data, &OfficialTagRepos{
OrgID: orgID,
TagID: tagID,
RepoID: repoId,
})
}
_, err = sess.Insert(&data)
if err != nil {
sess.Rollback()
return err
}
return sess.Commit()
}

func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) {
t := make([]TagReposBrief, 0)
const SQLCmd = "select t1.id as repo_id,t1.name as repo_name,t2.id as tag_id from repository t1 left join official_tag_repos t2 on (t1.id = t2.repo_id and t2.tag_id = ?) where t1.owner_id = ? and t1.is_private = false order by t1.updated_unix desc"

if err := x.SQL(SQLCmd, tagID, orgID).Find(&t); err != nil {
return nil, err
}
r := make([]TagReposSelected, 0)
for _, v := range t {
selected := false
if v.TagID > 0 {
selected = true
}
r = append(r, TagReposSelected{
RepoID: v.RepoID,
RepoName: v.RepoName,
Selected: selected,
})
}
return r, nil
}

func GetAllOfficialTagRepos(orgID int64, isOwner bool) ([]TagsDetail, error) {
result := make([]TagsDetail, 0)
tags, err := GetAllOfficialTags()
if err != nil {
return nil, err
}
for _, tag := range tags {
repos, err := GetOfficialTagDetail(orgID, tag.ID)
if err != nil {
return nil, err
}
if len(repos) == 0 && !isOwner {
continue
}
result = append(result, TagsDetail{
TagId: tag.ID,
TagName: tag.Name,
TagLimit: tag.Limit,
RepoList: repos,
})
}
return result, nil
}

func GetOfficialTagDetail(orgID, tagId int64) ([]Repository, error) {
t := make([]Repository, 0)
const SQLCmd = "select t2.* from official_tag_repos t1 inner join repository t2 on t1.repo_id = t2.id where t1.org_id = ? and t1.tag_id=? order by t2.updated_unix desc"

if err := x.SQL(SQLCmd, orgID, tagId).Find(&t); err != nil {
return nil, err
}
return t, nil
}

func GetAllOfficialTags() ([]OfficialTag, error) {
//todo redis?
o := make([]OfficialTag, 0)
err := x.Where("status = ?", 0).Find(&o)
if err != nil {
log.Error("GetAllOfficialTags error,%v", err)
return nil, err
}
return o, nil
}

+ 4
- 0
modules/auth/org.go View File

@@ -70,3 +70,7 @@ type CreateTeamForm struct {
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type SubmitReposOfTagForm struct {
RepoList []int64
}

+ 3
- 0
options/locale/locale_en-US.ini View File

@@ -222,6 +222,7 @@ contributors = Contributors

[explore]
repos = Repositories
select_repos = Select the project
users = Users
organizations = Organizations
images = CloudImages
@@ -234,6 +235,8 @@ org_no_results = No matching organizations found.
code_no_results = No source code matching your search term found.
code_search_results = Search results for '%s'
code_last_indexed_at = Last indexed %s
save=save
cancel=cancel

[auth]
create_new_account = Register Account


+ 3
- 0
options/locale/locale_zh-CN.ini View File

@@ -224,6 +224,7 @@ contributors=贡献者

[explore]
repos=项目
select_repos=精选项目
users=用户
organizations=组织
images = 云脑镜像
@@ -238,6 +239,8 @@ org_no_results=未找到匹配的组织。
code_no_results=未找到与搜索字词匹配的源代码。
code_search_results=“%s” 的搜索结果是
code_last_indexed_at=最后索引于 %s
save=保存
cancel=取消

[auth]
create_new_account=注册帐号


+ 8
- 0
routers/org/home.go View File

@@ -130,5 +130,13 @@ func Home(ctx *context.Context) {
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

//find org tag info
tags, err := models.GetAllOfficialTagRepos(org.ID, ctx.Org.IsOwner)
if err != nil {
ctx.ServerError("GetAllOfficialTagRepos", err)
return
}

ctx.Data["tags"] = tags
ctx.HTML(200, tplOrgHome)
}

+ 98
- 0
routers/org/tag.go View File

@@ -0,0 +1,98 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package org

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
"errors"
"strconv"
)

// SubmitTags submit repos of org tag
func SubmitTags(ctx *context.Context, form auth.SubmitReposOfTagForm) {
org, tag := getOrgAndTagFromContext(ctx)
if ctx.Written() {
return
}

err := models.UpdateTagReposByID(tag.ID, org.ID, form.RepoList)
if err != nil {
ctx.ServerError("UpdateTagReposByID", err)
return
}

ctx.JSON(200, map[string]interface{}{
"code": "00",
"msg": "success",
})
}

// GetTagRepos get repos under org tag
func GetTagRepos(ctx *context.Context) {
org, tag := getOrgAndTagFromContext(ctx)
if ctx.Written() {
return
}

r, err := models.GetTagRepos(tag.ID, org.ID)
if err != nil {
ctx.ServerError("GetTagRepos", err)
return
}

ctx.JSON(200, map[string]interface{}{
"code": "00",
"msg": "success",
"data": r,
})
}

// getDashboardContextUser finds out dashboard is viewing as which context user.
func getOrgAndTagFromContext(ctx *context.Context) (*models.User, *models.OfficialTag) {

var org *models.User
var tag *models.OfficialTag
var err error

orgName := ctx.Params(":org")

if len(orgName) > 0 {
// Organization.
org, err = models.GetUserByName(orgName)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.NotFound("GetUserByName", err)
} else {
ctx.ServerError("GetUserByName", err)
}
return nil, nil
}
if !org.IsOrganization() {
ctx.ServerError("GetUserByName", errors.New("it is not an organization"))
return nil, nil
}
}

tagIdStr := ctx.Query("tagId")
if len(tagIdStr) == 0 {
ctx.ServerError("GetTagInfo", errors.New("tag is not exist"))
return nil, nil
}
tagId, _ := strconv.ParseInt(tagIdStr, 10, 32)
tag, err = models.GetTagByID(tagId)
if err != nil {
if models.IsErrTagNotExist(err) {
ctx.NotFound("GetTagInfo", err)
} else {
ctx.ServerError("GetTagInfo", err)
}
return nil, nil
}

return org, tag
}

+ 4
- 0
routers/routes/routes.go View File

@@ -627,6 +627,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/org", func() {
m.Group("/:org", func() {
m.Get("/members", org.Members)
m.Group("/org_tag", func() {
m.Get("/repo_list", org.GetTagRepos)
m.Post("/repo_submit", bindIgnErr(auth.SubmitReposOfTagForm{}), org.SubmitTags)
})
}, context.OrgAssignment())
})
m.Group("/org", func() {


+ 5
- 4
templates/org/home.tmpl View File

@@ -20,10 +20,11 @@
</div>

<div class="ui container">
<div class="ui stackable grid">
{{template "org/navber" .}}
<div class="ui fourteen wide computer column">
{{template "org/navber" .}}
{{template "org/select_pro" .}}
<div class="ui stackable grid">
<div class="ui sixteen wide computer column">
<div class="ui mobile reversed stackable grid">
<div class="ui ten wide tablet eleven wide computer column">
{{if .CanCreateOrgRepo}}


+ 3
- 2
templates/org/member/members.tmpl View File

@@ -3,10 +3,11 @@
{{template "org/header" .}}
<div class="ui container">
{{template "base/alert" .}}
{{template "org/navber" .}}
<div class="ui stackable grid">
{{template "org/navber" .}}
<div class="ui fourteen wide computer column list">
<div class="ui sixteen wide computer column list">
{{ range .Members}}
<div class="item ui grid">
<div class="ui two wide column">


+ 37
- 5
templates/org/navber.tmpl View File

@@ -1,4 +1,4 @@
<div class="tablet only mobile only sixteen wide mobile sixteen wide tablet column row">
<!-- <div class="tablet only mobile only sixteen wide mobile sixteen wide tablet column row">
<div class="ui secondary pointing tabular top attached borderless menu navbar">
<a class="{{if $.PageIsOrgHome}}active{{end}} item" href="{{.HomeLink}}">
{{svg "octicon-home" 16}}&nbsp;{{$.i18n.Tr "org.home"}}
@@ -12,10 +12,10 @@
</a>
{{end}}
</div>
</div>
</div> -->
<!--平板、移动端-->
<div class="computer only two wide computer column">
<!-- <div class="computer only two wide computer column">
<div class="ui grid">
<div class="sixteen wide column ui secondary sticky pointing tabular vertical menu">
{{with .Org}}
@@ -33,5 +33,37 @@
{{end}}
</div>
</div>
</div>
<!--电脑、宽屏-->
</div> -->
<!--电脑、宽屏-->
<style>
.dis{
margin-bottom: 10px;
}
.active{
color:#0366D6 !important;
}
.mleft{
margin-left: 30% !important;
}
.mbom{
margin-bottom: 10px !important;
}
</style>

<div class="row">
<div class="ui secondary pointing tabular top attached borderless menu navbar mbom">
{{with .Org}}
<a class="{{if $.PageIsOrgHome}}active{{end}} item mleft" href="{{.HomeLink}}">
{{svg "octicon-home" 16}}&nbsp;{{$.i18n.Tr "org.home"}}
</a>
{{end}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
{{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}}
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams">
{{svg "octicon-jersey" 16}}&nbsp;{{$.i18n.Tr "org.teams"}}
</a>
{{end}}
</div>
</div>

+ 288
- 0
templates/org/select_pro.tmpl View File

@@ -0,0 +1,288 @@
<style>
.text-right{
float:right !important;
}
.header{
font-weight:bold;
font-size: 18px;
font-family: SourceHanSansSC-medium;
}
.cor{
color:#0366D6 !important;
}
.header_card{
/* color:#003A8C !important; */
color:#0366D6 !important;
margin: 10px 0;
}
.marg{
margin: 0 5px !important;
}

.content_list{
max-height: 200px;
overflow: auto;
}
.Relist{
color:#0366D6 !important;
}
.descript_height{
color: #101010 !important;
height: 40px !important;
word-break:break-all;
line-height: 20px;
overflow: hidden;
/* overflow: hidden!important;
word-wrap:break-word!important; */


}

.tags_height{
height: 30px !important;
}
.full_height{
height: 100%;
}
/deep/ ui.checkbox input[type=checkbox]::after{
border: 1px solid #0366D6 !important;
}
</style>
<div class="ui stackable grid">
<div style="width: 100%;margin:15px 0;">
{{if .tags}}
<span class="header">
精选项目
</span>
<!-- {{.IsOrganizationOwner}} -->
{{if .IsOrganizationOwner}}
<a class="text-right" id="model" onclick="showcreate()" >{{svg "octicon-gear" 16}}自定义</a>
{{end}}
{{end}}
</div>
<div style="width: 100%;">
{{ range .tags}}
{{if eq .TagName "精选项目"}}
<div class="ui three cards" style="margin-bottom: 10px;">
{{ range .RepoList}}
<div class="card" >
<div class="extra full_height" >
<div class=" header ">
<a class="header_card" href="{{.Link}}"> {{.Name}}</a>
</div>
<div class='content descript_height'>
{{.Description}}
</div>
<div class="content tags_height" style="margin-top: 5px;">
{{if .Topics }}
<div class=" ui tags ">
{{range .Topics}}
{{if ne . "" }}<a href="{{AppSubUrl}}/explore/repos?q={{.}}&topic={{$.Topic}}"><div class="ui small label topic">{{.}}</div></a>{{end}}
{{end}}
</div>
{{end}}
</div>
<div class="ui mini right compact marg" >
<a class="item marg ">
{{svg "octicon-eye" 16}} {{.NumWatches}}
</a>
<a class="item marg">
{{svg "octicon-star" 16}} {{.NumStars}}
</a>
<a class="item marg">
{{svg "octicon-git-branch" 16}} {{.NumForks}}
</a>
</div>

</div>
</div>
{{end}}
</div>
{{end}}
{{end}}

</div>

</div>

<div class="ui modal">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4 id="model_header">自定义精选项目</h4>
</div>
<div class="content content-padding" style="color: black;">
<p>对多可选9个公开项目</p>
<div class="ui search" >
<div class="ui input" style="width: 100%;">
<input type="text" id = 'search_selectPro' placeholder="Search ..." value = '' oninput="search()">
</div>
</div>
<div style="margin: 10px ;">
<div id ='org_list' style="margin-bottom: 20px;"class="content_list" >

</div>
</div>
<p id='recommend'></p>
<div class="inline field" style="margin-left: 30%;">
<div class="actions">
<button id="submitId" type="button" class="ui create_train_job green deny button" onclick="saveSeletedPro(1)">
{{.i18n.Tr "explore.save"}}
</button>
<button class="ui button cancel" >{{.i18n.Tr "explore.cancel"}}</button>
</div>
</div>
</div>

</div>

<script>
var data;
var filterData=[];
var num=0;

function showcreate(obj){
$('.ui.modal')
.modal({
centered: false,
onShow:function(){
$("#org_list").empty()
getPro(1)
},
onHide:function(){
}
})
.modal('show')
}
function getPro(typeTag){

$.ajax({
type:"GET",
url:"/org/{{.Org.DisplayName}}/org_tag/repo_list?tagId="+typeTag,
dataType:"json",
async:false,
success:function(json){
data = json.data;
var n_length = data.length
pro_html = getHTML(data)
$("#org_list").append(pro_html)
console.log('原始',data)
checkedNum()
}
});
}
function getHTML(data){
let pro_html=''
for (let i=0;i<data.length;i++){
if (data[i].Selected==true){
console.log("data[i]:",data[i])
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" checked="" onclick="checkedNum()" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected=${data[i].Selected} > <label> ${data[i].RepoName} </label></div>`
pro_html += '</div>'
}
else{
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" onclick="checkedNum()" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected= ${data[i].Selected}> <label> ${data[i].RepoName} </label></div>`
pro_html += '</div>'
}
}
return pro_html
}
function saveSeletedPro(typeTag){
var saveData=[];
$('input[name="select_pro_name"]:checked').each(function(){
console.log('值',this.dataset.repoid)

saveData.push(parseInt(this.dataset.repoid));
})
// saveData = getSelecteDataID();
// console.log("数据:",saveData)
$.ajax({
type:"POST",
url:"/org/{{.Org.DisplayName}}/org_tag/repo_submit?tagId="+typeTag,
contentType:'application/json',
dataType:"json",
async:false,
data:JSON.stringify({'repoList':saveData
}),
success:function(res){
console.log('保存成功');
location.reload()

}
});
}

function getSelecteData(){
var selectedData=[];
$('input[name="select_pro_name"]:checked').each(function(){
// console.log(this)
console.log('值',this.dataset.selected)

selectedData.push({"RepoID":parseInt(this.dataset.repoid),"RepoName":this.dataset.reponame,"Selected":JSON.parse(this.dataset.selected)});
})
return selectedData
}
function search(){
var selectedData = getSelecteData();
var searchValue = document.getElementById("search_selectPro").value;
filterData=[];
console.log("searchValue:",searchValue)
for (let i=0;i<data.length;i++){
var isInclude=false;
if(data[i].RepoName.toLowerCase().includes(searchValue.toLowerCase())){
filterData.push(data[i])
}
}
console.log("选中的值:",selectedData)
console.log("筛选包括选中的值:",filterData)
var showData=[];
for(i=0;i<selectedData.length;i++){
filterData =filterData.filter((item)=>{
return item.RepoID!=selectedData[i].RepoID
});
}
console.log("筛选后不包括选中的值:",filterData)
$("#org_list").empty()
if(searchValue!=""){
if (filterData.length!=0){
var pro_html = getHTML(selectedData);
console.log("selectedData_pro_html:",pro_html)
$("#org_list").append(pro_html)
pro_html= getHTML(filterData);
$("#org_list").append(pro_html)
}else{
var pro_html = getHTML(selectedData);
$("#org_list").append(pro_html)
}
}else{
var pro_html = getHTML(data);
$("#org_list").append(pro_html)
}
}

function checkedNum(){
num=0;
var inputs = document.getElementsByName("select_pro_name")
for (var i=0;i<inputs.length;i++){
if(inputs[i].checked){
num++
}
}
var show_num = 9-num;
document.getElementById("recommend").innerHTML="还能推荐"+show_num+"个"

}

</script>

+ 3
- 3
templates/org/team/teams.tmpl View File

@@ -3,12 +3,12 @@
{{template "org/header" .}}
<div class="ui container">
{{template "base/alert" .}}
{{template "org/navber" .}}
<div class="ui stackable grid">
{{template "org/navber" .}}
<div class="ui fourteen wide computer column list">
<div class="ui sixteen wide computer column list">
<div class="ui two column grid">
{{range .Teams}}
<div class="column">


Loading…
Cancel
Save