| @@ -38,6 +38,7 @@ type Attachment struct { | |||||
| UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added | UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added | ||||
| CommentID int64 | CommentID int64 | ||||
| Name string | Name string | ||||
| Description string `xorm:"TEXT"` | |||||
| DownloadCount int64 `xorm:"DEFAULT 0"` | DownloadCount int64 `xorm:"DEFAULT 0"` | ||||
| Size int64 `xorm:"DEFAULT 0"` | Size int64 `xorm:"DEFAULT 0"` | ||||
| IsPrivate bool `xorm:"DEFAULT false"` | IsPrivate bool `xorm:"DEFAULT false"` | ||||
| @@ -0,0 +1,16 @@ | |||||
| package models | |||||
| type BaseMessage struct { | |||||
| Code int | |||||
| Message string | |||||
| } | |||||
| var BaseOKMessage = BaseMessage{ | |||||
| 0, "", | |||||
| } | |||||
| func BaseErrorMessage(message string) BaseMessage { | |||||
| return BaseMessage{ | |||||
| 1, message, | |||||
| } | |||||
| } | |||||
| @@ -22,6 +22,7 @@ type Dataset struct { | |||||
| Category string | Category string | ||||
| Description string `xorm:"TEXT"` | Description string `xorm:"TEXT"` | ||||
| DownloadTimes int64 | DownloadTimes int64 | ||||
| NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
| License string | License string | ||||
| Task string | Task string | ||||
| ReleaseID int64 `xorm:"INDEX"` | ReleaseID int64 `xorm:"INDEX"` | ||||
| @@ -91,6 +92,9 @@ type SearchDatasetOptions struct { | |||||
| OwnerID int64 | OwnerID int64 | ||||
| RepoID int64 | RepoID int64 | ||||
| IncludePublic bool | IncludePublic bool | ||||
| Category string | |||||
| Task string | |||||
| License string | |||||
| ListOptions | ListOptions | ||||
| SearchOrderBy | SearchOrderBy | ||||
| IsOwner bool | IsOwner bool | ||||
| @@ -104,22 +108,6 @@ func CreateDataset(dataset *Dataset) (err error) { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func CreateDefaultDatasetToRepo(repo *Repository) (err error) { | |||||
| dataset := &Dataset{RepoID: repo.ID} | |||||
| has, err := x.Get(dataset) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if !has { | |||||
| dataset.Status = DatasetStatusPrivate | |||||
| dataset.Title = repo.Name | |||||
| if err = CreateDataset(dataset); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) { | func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) { | ||||
| cond := SearchDatasetCondition(opts) | cond := SearchDatasetCondition(opts) | ||||
| return SearchDatasetByCondition(opts, cond) | return SearchDatasetByCondition(opts, cond) | ||||
| @@ -133,12 +121,24 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||||
| cond = cond.And(builder.Like{"dataset.title", opts.Keyword}) | cond = cond.And(builder.Like{"dataset.title", opts.Keyword}) | ||||
| } | } | ||||
| if len(opts.Category) > 0 { | |||||
| cond = cond.And(builder.Eq{"dataset.category": opts.Category}) | |||||
| } | |||||
| if len(opts.Task) > 0 { | |||||
| cond = cond.And(builder.Eq{"dataset.task": opts.Task}) | |||||
| } | |||||
| if len(opts.License) > 0 { | |||||
| cond = cond.And(builder.Eq{"dataset.license": opts.License}) | |||||
| } | |||||
| if opts.RepoID > 0 { | if opts.RepoID > 0 { | ||||
| cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID}) | cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID}) | ||||
| } | } | ||||
| if opts.IncludePublic { | if opts.IncludePublic { | ||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | ||||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||||
| if opts.OwnerID > 0 { | if opts.OwnerID > 0 { | ||||
| if len(opts.Keyword) == 0 { | if len(opts.Keyword) == 0 { | ||||
| cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | ||||
| @@ -153,6 +153,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||||
| cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | ||||
| if !opts.IsOwner { | if !opts.IsOwner { | ||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | ||||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||||
| } | } | ||||
| } | } | ||||
| @@ -169,14 +170,19 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da | |||||
| defer sess.Close() | defer sess.Close() | ||||
| datasets := make(DatasetList, 0, opts.PageSize) | datasets := make(DatasetList, 0, opts.PageSize) | ||||
| selectColumnsSql := "distinct dataset.id,dataset.title, dataset.status, dataset.category, dataset.description, dataset.download_times, dataset.license, dataset.task, dataset.release_id, dataset.user_id, dataset.repo_id, dataset.created_unix,dataset.updated_unix,dataset.num_stars" | |||||
| count, err := sess.Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).Count(new(Dataset)) | |||||
| count, err := sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||||
| Where(cond).Count(new(Dataset)) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, 0, fmt.Errorf("Count: %v", err) | return nil, 0, fmt.Errorf("Count: %v", err) | ||||
| } | } | ||||
| sess.Select("dataset.*").Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||||
| sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||||
| Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||||
| if opts.PageSize > 0 { | if opts.PageSize > 0 { | ||||
| sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | ||||
| } | } | ||||
| @@ -231,13 +237,23 @@ func getDatasetAttachments(e Engine, typeCloudBrain int, isSigned bool, user *Us | |||||
| sort.Sort(sortedRels) | sort.Sort(sortedRels) | ||||
| // Select attachments | // Select attachments | ||||
| err = e. | |||||
| Asc("dataset_id"). | |||||
| In("dataset_id", sortedRels.ID). | |||||
| And("type = ?", typeCloudBrain). | |||||
| Find(&attachments, Attachment{}) | |||||
| if err != nil { | |||||
| return err | |||||
| if typeCloudBrain == -1 { | |||||
| err = e. | |||||
| Asc("dataset_id"). | |||||
| In("dataset_id", sortedRels.ID). | |||||
| Find(&attachments, Attachment{}) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| err = e. | |||||
| Asc("dataset_id"). | |||||
| In("dataset_id", sortedRels.ID). | |||||
| And("type = ?", typeCloudBrain). | |||||
| Find(&attachments, Attachment{}) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | } | ||||
| // merge join | // merge join | ||||
| @@ -301,9 +317,6 @@ func GetDatasetByID(id int64) (*Dataset, error) { | |||||
| } | } | ||||
| func GetDatasetByRepo(repo *Repository) (*Dataset, error) { | func GetDatasetByRepo(repo *Repository) (*Dataset, error) { | ||||
| if err := CreateDefaultDatasetToRepo(repo); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| dataset := &Dataset{RepoID: repo.ID} | dataset := &Dataset{RepoID: repo.ID} | ||||
| has, err := x.Get(dataset) | has, err := x.Get(dataset) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -0,0 +1,62 @@ | |||||
| package models | |||||
| import "code.gitea.io/gitea/modules/timeutil" | |||||
| type DatasetStar struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UID int64 `xorm:"UNIQUE(s)"` | |||||
| DatasetID int64 `xorm:"UNIQUE(s)"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
| } | |||||
| // StarRepo or unstar repository. | |||||
| func StarDataset(userID, datasetID int64, star bool) error { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| if err := sess.Begin(); err != nil { | |||||
| return err | |||||
| } | |||||
| if star { | |||||
| if isDatasetStaring(sess, userID, datasetID) { | |||||
| return nil | |||||
| } | |||||
| if _, err := sess.Insert(&DatasetStar{UID: userID, DatasetID: datasetID}); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `dataset` SET num_stars = num_stars + 1 WHERE id = ?", datasetID); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `user` SET num_dataset_stars = num_dataset_stars + 1 WHERE id = ?", userID); err != nil { | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| if !isDatasetStaring(sess, userID, datasetID) { | |||||
| return nil | |||||
| } | |||||
| if _, err := sess.Delete(&DatasetStar{0, userID, datasetID, 0}); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `dataset` SET num_stars = num_stars - 1 WHERE id = ?", datasetID); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `user` SET num_dataset_stars = num_dataset_stars - 1 WHERE id = ?", userID); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return sess.Commit() | |||||
| } | |||||
| func IsDatasetStaring(userID, datasetID int64) bool { | |||||
| return isDatasetStaring(x, userID, datasetID) | |||||
| } | |||||
| func isDatasetStaring(e Engine, userID, datasetID int64) bool { | |||||
| has, _ := e.Get(&DatasetStar{0, userID, datasetID, 0}) | |||||
| return has | |||||
| } | |||||
| @@ -1280,10 +1280,6 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr | |||||
| return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err) | return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err) | ||||
| } | } | ||||
| if err = CreateDefaultDatasetToRepo(repo); err != nil { | |||||
| return fmt.Errorf("models.CreateDefaultDatasetToRepo: %v", err) | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -1586,6 +1582,25 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| //If repo has become private, we need set dataset and dataset_file to private | |||||
| _, err = e.Where("repo_id = ?", repo.ID).Cols("status").Update(&Dataset{ | |||||
| Status: 0, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| dataset, err := GetDatasetByRepo(repo) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| _, err = e.Where("dataset_id = ?", dataset.ID).Cols("is_private").Update(&Attachment{ | |||||
| IsPrivate: true, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | } | ||||
| // Create/Remove git-daemon-export-ok for git-daemon... | // Create/Remove git-daemon-export-ok for git-daemon... | ||||
| @@ -153,10 +153,11 @@ type User struct { | |||||
| UseCustomAvatar bool | UseCustomAvatar bool | ||||
| // Counters | // Counters | ||||
| NumFollowers int | |||||
| NumFollowing int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumStars int | |||||
| NumRepos int | |||||
| NumFollowers int | |||||
| NumFollowing int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumStars int | |||||
| NumDatasetStars int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepos int | |||||
| // For organization | // For organization | ||||
| NumTeams int | NumTeams int | ||||
| @@ -9,11 +9,10 @@ import ( | |||||
| type CreateDatasetForm struct { | type CreateDatasetForm struct { | ||||
| Title string `binding:"Required"` | Title string `binding:"Required"` | ||||
| Category string `binding:"Required"` | Category string `binding:"Required"` | ||||
| Description string `binding:"Required;MaxSize(254)"` | |||||
| Description string `binding:"Required"` | |||||
| License string `binding:"Required;MaxSize(64)"` | License string `binding:"Required;MaxSize(64)"` | ||||
| Task string `binding:"Required;MaxSize(64)"` | Task string `binding:"Required;MaxSize(64)"` | ||||
| ReleaseID int64 `xorm:"INDEX"` | ReleaseID int64 `xorm:"INDEX"` | ||||
| Private bool | |||||
| Files []string | Files []string | ||||
| } | } | ||||
| @@ -25,11 +24,10 @@ type EditDatasetForm struct { | |||||
| ID int64 `binding:"Required"` | ID int64 `binding:"Required"` | ||||
| Title string `binding:"Required"` | Title string `binding:"Required"` | ||||
| Category string `binding:"Required"` | Category string `binding:"Required"` | ||||
| Description string `binding:"Required;MaxSize(254)"` | |||||
| Description string `binding:"Required"` | |||||
| License string `binding:"Required;MaxSize(64)"` | License string `binding:"Required;MaxSize(64)"` | ||||
| Task string `binding:"Required;MaxSize(64)"` | Task string `binding:"Required;MaxSize(64)"` | ||||
| Private bool | |||||
| ReleaseID int64 `xorm:"INDEX"` | |||||
| ReleaseID int64 `xorm:"INDEX"` | |||||
| Files []string | Files []string | ||||
| Type string `binding:"Required"` | |||||
| Type string `binding:"Required"` | |||||
| } | } | ||||
| @@ -709,8 +709,12 @@ alert = To initiate a cloud brain task, please upload the dataset in zip format. | |||||
| dataset = Dataset | dataset = Dataset | ||||
| dataset_setting= Dataset Setting | dataset_setting= Dataset Setting | ||||
| title = Name | title = Name | ||||
| title_format_err=Name can only contain number,letter,'-','_' or '.', and can be up to 100 characters long. | |||||
| description = Description | description = Description | ||||
| description_format_err=Description's length can be up to 1024 characters long. | |||||
| create_dataset = Create Dataset | create_dataset = Create Dataset | ||||
| create_dataset_fail=Failed to create dataset. | |||||
| query_dataset_fail=Failed to query dataset. | |||||
| show_dataset= Dataset | show_dataset= Dataset | ||||
| edit_dataset= Edit Dataset | edit_dataset= Edit Dataset | ||||
| update_dataset= Update Dataset | update_dataset= Update Dataset | ||||
| @@ -1061,6 +1065,7 @@ unstar = Unstar | |||||
| star = Star | star = Star | ||||
| fork = Fork | fork = Fork | ||||
| download_archive = Download Repository | download_archive = Download Repository | ||||
| star_fail=Failed to %s the dataset. | |||||
| no_desc = No Description | no_desc = No Description | ||||
| no_label = No labels | no_label = No labels | ||||
| @@ -712,8 +712,13 @@ alert=如果要发起云脑任务,请上传zip格式的数据集 | |||||
| dataset=数据集 | dataset=数据集 | ||||
| dataset_setting=数据集设置 | dataset_setting=数据集设置 | ||||
| title=名称 | title=名称 | ||||
| title_format_err=名称最多允许输入100个字符,只允许字母,数字,中划线 (‘-’),下划线 (‘_’) 和点 (‘.’) 。 | |||||
| description=描述 | description=描述 | ||||
| description_format_err=描述最多允许输入1024个字符。 | |||||
| create_dataset=创建数据集 | create_dataset=创建数据集 | ||||
| create_dataset_fail=创建数据集失败。 | |||||
| query_dataset_fail=查询数据集失败 | |||||
| show_dataset=数据集 | show_dataset=数据集 | ||||
| edit_dataset=编辑数据集 | edit_dataset=编辑数据集 | ||||
| update_dataset=更新数据集 | update_dataset=更新数据集 | ||||
| @@ -1070,6 +1075,8 @@ unstar=取消点赞 | |||||
| star=点赞 | star=点赞 | ||||
| fork=派生 | fork=派生 | ||||
| download_archive=下载此项目 | download_archive=下载此项目 | ||||
| star_fail=%s失败。 | |||||
| no_desc=暂无描述 | no_desc=暂无描述 | ||||
| no_label = 暂无标签 | no_label = 暂无标签 | ||||
| @@ -301,6 +301,10 @@ func ExploreDatasets(ctx *context.Context) { | |||||
| orderBy = models.SearchOrderBySizeReverse | orderBy = models.SearchOrderBySizeReverse | ||||
| case "downloadtimes": | case "downloadtimes": | ||||
| orderBy = models.SearchOrderByDownloadTimes | orderBy = models.SearchOrderByDownloadTimes | ||||
| case "moststars": | |||||
| orderBy = models.SearchOrderByStarsReverse | |||||
| case "feweststars": | |||||
| orderBy = models.SearchOrderByStars | |||||
| default: | default: | ||||
| ctx.Data["SortType"] = "recentupdate" | ctx.Data["SortType"] = "recentupdate" | ||||
| orderBy = models.SearchOrderByRecentUpdated | orderBy = models.SearchOrderByRecentUpdated | ||||
| @@ -308,6 +312,9 @@ func ExploreDatasets(ctx *context.Context) { | |||||
| keyword := strings.Trim(ctx.Query("q"), " ") | keyword := strings.Trim(ctx.Query("q"), " ") | ||||
| category := ctx.Query("category") | |||||
| task := ctx.Query("task") | |||||
| license := ctx.Query("license") | |||||
| var ownerID int64 | var ownerID int64 | ||||
| if ctx.User != nil && !ctx.User.IsAdmin { | if ctx.User != nil && !ctx.User.IsAdmin { | ||||
| ownerID = ctx.User.ID | ownerID = ctx.User.ID | ||||
| @@ -316,6 +323,9 @@ func ExploreDatasets(ctx *context.Context) { | |||||
| Keyword: keyword, | Keyword: keyword, | ||||
| IncludePublic: true, | IncludePublic: true, | ||||
| SearchOrderBy: orderBy, | SearchOrderBy: orderBy, | ||||
| Category: category, | |||||
| Task: task, | |||||
| License: license, | |||||
| OwnerID: ownerID, | OwnerID: ownerID, | ||||
| ListOptions: models.ListOptions{ | ListOptions: models.ListOptions{ | ||||
| Page: page, | Page: page, | ||||
| @@ -331,6 +341,9 @@ func ExploreDatasets(ctx *context.Context) { | |||||
| pager := context.NewPagination(int(count), opts.PageSize, page, 5) | pager := context.NewPagination(int(count), opts.PageSize, page, 5) | ||||
| ctx.Data["Keyword"] = opts.Keyword | ctx.Data["Keyword"] = opts.Keyword | ||||
| ctx.Data["Category"] = category | |||||
| ctx.Data["Task"] = task | |||||
| ctx.Data["License"] = license | |||||
| pager.SetDefaultParams(ctx) | pager.SetDefaultParams(ctx) | ||||
| ctx.Data["Page"] = pager | ctx.Data["Page"] = pager | ||||
| @@ -15,6 +15,8 @@ import ( | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "code.gitea.io/gitea/modules/base" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/labelmsg" | "code.gitea.io/gitea/modules/labelmsg" | ||||
| @@ -30,8 +32,9 @@ import ( | |||||
| const ( | const ( | ||||
| //result of decompress | //result of decompress | ||||
| DecompressSuccess = "0" | |||||
| DecompressFailed = "1" | |||||
| DecompressSuccess = "0" | |||||
| DecompressFailed = "1" | |||||
| tplAttachmentUpload base.TplName = "repo/attachment/upload" | |||||
| ) | ) | ||||
| type CloudBrainDataset struct { | type CloudBrainDataset struct { | ||||
| @@ -63,6 +66,12 @@ func renderAttachmentSettings(ctx *context.Context) { | |||||
| ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles | ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles | ||||
| } | } | ||||
| func UploadAttachmentUI(ctx *context.Context) { | |||||
| ctx.Data["datasetId"] = ctx.Query("datasetId") | |||||
| ctx.HTML(200, tplAttachmentUpload) | |||||
| } | |||||
| // UploadAttachment response for uploading issue's attachment | // UploadAttachment response for uploading issue's attachment | ||||
| func UploadAttachment(ctx *context.Context) { | func UploadAttachment(ctx *context.Context) { | ||||
| if !setting.Attachment.Enabled { | if !setting.Attachment.Enabled { | ||||
| @@ -387,11 +396,17 @@ func AddAttachment(ctx *context.Context) { | |||||
| ctx.Error(404, "attachment has not been uploaded") | ctx.Error(404, "attachment has not been uploaded") | ||||
| return | return | ||||
| } | } | ||||
| datasetId := ctx.QueryInt64("dataset_id") | |||||
| dataset, err := models.GetDatasetByID(datasetId) | |||||
| if err != nil { | |||||
| ctx.Error(404, "dataset does not exist.") | |||||
| return | |||||
| } | |||||
| attachment, err := models.InsertAttachment(&models.Attachment{ | attachment, err := models.InsertAttachment(&models.Attachment{ | ||||
| UUID: uuid, | UUID: uuid, | ||||
| UploaderID: ctx.User.ID, | UploaderID: ctx.User.ID, | ||||
| IsPrivate: true, | |||||
| IsPrivate: dataset.IsPrivate(), | |||||
| Name: fileName, | Name: fileName, | ||||
| Size: ctx.QueryInt64("size"), | Size: ctx.QueryInt64("size"), | ||||
| DatasetID: ctx.QueryInt64("dataset_id"), | DatasetID: ctx.QueryInt64("dataset_id"), | ||||
| @@ -830,22 +845,23 @@ func CompleteMultipart(ctx *context.Context) { | |||||
| ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err)) | ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err)) | ||||
| return | return | ||||
| } | } | ||||
| dataset, _ := models.GetDatasetByID(ctx.QueryInt64("dataset_id")) | |||||
| attachment, err := models.InsertAttachment(&models.Attachment{ | attachment, err := models.InsertAttachment(&models.Attachment{ | ||||
| UUID: uuid, | |||||
| UploaderID: ctx.User.ID, | |||||
| IsPrivate: true, | |||||
| Name: fileName, | |||||
| Size: ctx.QueryInt64("size"), | |||||
| DatasetID: ctx.QueryInt64("dataset_id"), | |||||
| Type: typeCloudBrain, | |||||
| UUID: uuid, | |||||
| UploaderID: ctx.User.ID, | |||||
| IsPrivate: dataset.IsPrivate(), | |||||
| Name: fileName, | |||||
| Size: ctx.QueryInt64("size"), | |||||
| DatasetID: ctx.QueryInt64("dataset_id"), | |||||
| Description: ctx.Query("description"), | |||||
| Type: typeCloudBrain, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err)) | ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err)) | ||||
| return | return | ||||
| } | } | ||||
| dataset, _ := models.GetDatasetByID(attachment.DatasetID) | |||||
| repository, _ := models.GetRepositoryByID(dataset.RepoID) | repository, _ := models.GetRepositoryByID(dataset.RepoID) | ||||
| notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(attachment.Type), attachment.Name, models.ActionUploadAttachment) | notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(attachment.Type), attachment.Name, models.ActionUploadAttachment) | ||||
| @@ -1,7 +1,11 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "regexp" | |||||
| "sort" | "sort" | ||||
| "strconv" | |||||
| "unicode/utf8" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| @@ -12,9 +16,13 @@ import ( | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| tplIndex base.TplName = "repo/datasets/index" | |||||
| tplIndex base.TplName = "repo/datasets/index" | |||||
| tplDatasetCreate base.TplName = "repo/datasets/create" | |||||
| tplDatasetEdit base.TplName = "repo/datasets/edit" | |||||
| ) | ) | ||||
| var titlePattern = regexp.MustCompile(`^[A-Za-z0-9-_\\.]{1,100}$`) | |||||
| // MustEnableDataset check if repository enable internal dataset | // MustEnableDataset check if repository enable internal dataset | ||||
| func MustEnableDataset(ctx *context.Context) { | func MustEnableDataset(ctx *context.Context) { | ||||
| if !ctx.Repo.CanRead(models.UnitTypeDatasets) { | if !ctx.Repo.CanRead(models.UnitTypeDatasets) { | ||||
| @@ -84,21 +92,11 @@ func QueryDataSet(ctx *context.Context) []*models.Attachment { | |||||
| attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo) | attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo) | ||||
| ctx.Data["SortType"] = ctx.Query("sort") | ctx.Data["SortType"] = ctx.Query("sort") | ||||
| switch ctx.Query("sort") { | |||||
| case "newest": | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| case "oldest": | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix < attachments[j].CreatedUnix | |||||
| }) | |||||
| default: | |||||
| ctx.Data["SortType"] = "newest" | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| } | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| return attachments | return attachments | ||||
| } | } | ||||
| @@ -110,17 +108,16 @@ func DatasetIndex(ctx *context.Context) { | |||||
| dataset, err := models.GetDatasetByRepo(repo) | dataset, err := models.GetDatasetByRepo(repo) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("query dataset, not found repo.") | |||||
| ctx.NotFound("GetDatasetByRepo", err) | |||||
| log.Warn("query dataset, not found.") | |||||
| ctx.HTML(200, tplIndex) | |||||
| return | return | ||||
| } | } | ||||
| cloudbrainType := -1 | |||||
| if ctx.Query("type") != "" { | |||||
| if ctx.Query("type") == "" { | |||||
| log.Error("query dataset, not found param type") | |||||
| ctx.NotFound("type error", nil) | |||||
| return | |||||
| cloudbrainType = ctx.QueryInt("type") | |||||
| } | } | ||||
| err = models.GetDatasetAttachments(ctx.QueryInt("type"), ctx.IsSigned, ctx.User, dataset) | |||||
| err = models.GetDatasetAttachments(cloudbrainType, ctx.IsSigned, ctx.User, dataset) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetDatasetAttachments", err) | ctx.ServerError("GetDatasetAttachments", err) | ||||
| return | return | ||||
| @@ -128,53 +125,130 @@ func DatasetIndex(ctx *context.Context) { | |||||
| attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo) | attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo) | ||||
| ctx.Data["SortType"] = ctx.Query("sort") | |||||
| switch ctx.Query("sort") { | |||||
| case "newest": | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| case "oldest": | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix < attachments[j].CreatedUnix | |||||
| }) | |||||
| default: | |||||
| ctx.Data["SortType"] = "newest" | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| sort.Slice(attachments, func(i, j int) bool { | |||||
| return attachments[i].CreatedUnix > attachments[j].CreatedUnix | |||||
| }) | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| pagesize := ctx.QueryInt("pagesize") | |||||
| if pagesize <= 0 { | |||||
| pagesize = setting.UI.ExplorePagingNum | |||||
| } | } | ||||
| pager := context.NewPagination(len(attachments), pagesize, page, 5) | |||||
| pageAttachments := getPageAttachments(attachments, page, pagesize) | |||||
| ctx.Data["Page"] = pager | |||||
| ctx.Data["PageIsDataset"] = true | ctx.Data["PageIsDataset"] = true | ||||
| ctx.Data["Title"] = ctx.Tr("dataset.show_dataset") | ctx.Data["Title"] = ctx.Tr("dataset.show_dataset") | ||||
| ctx.Data["Link"] = ctx.Repo.RepoLink + "/datasets" | ctx.Data["Link"] = ctx.Repo.RepoLink + "/datasets" | ||||
| ctx.Data["dataset"] = dataset | ctx.Data["dataset"] = dataset | ||||
| ctx.Data["Attachments"] = attachments | |||||
| ctx.Data["Attachments"] = pageAttachments | |||||
| ctx.Data["IsOwner"] = true | ctx.Data["IsOwner"] = true | ||||
| ctx.Data["StoreType"] = setting.Attachment.StoreType | ctx.Data["StoreType"] = setting.Attachment.StoreType | ||||
| ctx.Data["Type"] = ctx.QueryInt("type") | |||||
| ctx.Data["Type"] = cloudbrainType | |||||
| renderAttachmentSettings(ctx) | renderAttachmentSettings(ctx) | ||||
| ctx.HTML(200, tplIndex) | ctx.HTML(200, tplIndex) | ||||
| } | } | ||||
| func getPageAttachments(attachments []*models.Attachment, page int, pagesize int) []*models.Attachment { | |||||
| begin := (page - 1) * pagesize | |||||
| end := (page) * pagesize | |||||
| if begin > len(attachments)-1 { | |||||
| return nil | |||||
| } | |||||
| if end > len(attachments)-1 { | |||||
| return attachments[begin:] | |||||
| } else { | |||||
| return attachments[begin:end] | |||||
| } | |||||
| } | |||||
| func CreateDataset(ctx *context.Context) { | |||||
| MustEnableDataset(ctx) | |||||
| ctx.HTML(200, tplDatasetCreate) | |||||
| } | |||||
| func EditDataset(ctx *context.Context) { | |||||
| MustEnableDataset(ctx) | |||||
| datasetId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64) | |||||
| dataset, _ := models.GetDatasetByID(datasetId) | |||||
| if dataset == nil { | |||||
| ctx.Error(http.StatusNotFound, "") | |||||
| return | |||||
| } | |||||
| ctx.Data["Dataset"] = dataset | |||||
| ctx.HTML(200, tplDatasetEdit) | |||||
| } | |||||
| func CreateDatasetPost(ctx *context.Context, form auth.CreateDatasetForm) { | |||||
| dataset := &models.Dataset{} | |||||
| if !titlePattern.MatchString(form.Title) { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err"))) | |||||
| return | |||||
| } | |||||
| if utf8.RuneCountInString(form.Description) > 1024 { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err"))) | |||||
| return | |||||
| } | |||||
| dataset.RepoID = ctx.Repo.Repository.ID | |||||
| dataset.UserID = ctx.User.ID | |||||
| dataset.Category = form.Category | |||||
| dataset.Task = form.Task | |||||
| dataset.Title = form.Title | |||||
| dataset.License = form.License | |||||
| dataset.Description = form.Description | |||||
| dataset.DownloadTimes = 0 | |||||
| if ctx.Repo.Repository.IsPrivate { | |||||
| dataset.Status = 0 | |||||
| } else { | |||||
| dataset.Status = 1 | |||||
| } | |||||
| err := models.CreateDataset(dataset) | |||||
| if err != nil { | |||||
| log.Error("fail to create dataset", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.create_dataset_fail"))) | |||||
| } else { | |||||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||||
| } | |||||
| } | |||||
| func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { | func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { | ||||
| ctx.Data["PageIsDataset"] = true | ctx.Data["PageIsDataset"] = true | ||||
| ctx.Data["Title"] = ctx.Tr("dataset.edit_dataset") | ctx.Data["Title"] = ctx.Tr("dataset.edit_dataset") | ||||
| if !titlePattern.MatchString(form.Title) { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err"))) | |||||
| return | |||||
| } | |||||
| if utf8.RuneCountInString(form.Description) > 1024 { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err"))) | |||||
| return | |||||
| } | |||||
| rel, err := models.GetDatasetByID(form.ID) | rel, err := models.GetDatasetByID(form.ID) | ||||
| ctx.Data["dataset"] = rel | ctx.Data["dataset"] = rel | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetDataset", err) | |||||
| return | |||||
| } | |||||
| if ctx.HasError() { | |||||
| ctx.Data["Error"] = true | |||||
| ctx.HTML(200, tplIndex) | |||||
| log.Error("failed to query dataset", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail"))) | |||||
| return | return | ||||
| } | } | ||||
| @@ -184,9 +258,25 @@ func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { | |||||
| rel.Task = form.Task | rel.Task = form.Task | ||||
| rel.License = form.License | rel.License = form.License | ||||
| if err = models.UpdateDataset(models.DefaultDBContext(), rel); err != nil { | if err = models.UpdateDataset(models.DefaultDBContext(), rel); err != nil { | ||||
| ctx.Data["Error"] = true | |||||
| ctx.HTML(200, tplIndex) | |||||
| log.Error("%v", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail"))) | |||||
| } | } | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/datasets?type=" + form.Type) | |||||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||||
| } | |||||
| func DatasetAction(ctx *context.Context) { | |||||
| var err error | |||||
| datasetId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64) | |||||
| switch ctx.Params(":action") { | |||||
| case "star": | |||||
| err = models.StarDataset(ctx.User.ID, datasetId, true) | |||||
| case "unstar": | |||||
| err = models.StarDataset(ctx.User.ID, datasetId, false) | |||||
| } | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.star_fail", ctx.Params(":action")))) | |||||
| } else { | |||||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||||
| } | |||||
| } | } | ||||
| @@ -245,10 +245,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||||
| // This section doesn't require repo_name/RepoName to be set in the form, don't show it | // This section doesn't require repo_name/RepoName to be set in the form, don't show it | ||||
| // as an error on the UI for this action | // as an error on the UI for this action | ||||
| ctx.Data["Err_RepoName"] = nil | ctx.Data["Err_RepoName"] = nil | ||||
| if err := models.CreateDefaultDatasetToRepo(repo); err != nil { | |||||
| ctx.ServerError("CreateDefaultDatasetToRepo", err) | |||||
| return | |||||
| } | |||||
| if form.EnableDataset && !models.UnitTypeDatasets.UnitGlobalDisabled() { | if form.EnableDataset && !models.UnitTypeDatasets.UnitGlobalDisabled() { | ||||
| units = append(units, models.RepoUnit{ | units = append(units, models.RepoUnit{ | ||||
| @@ -586,6 +586,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/delete", repo.DeleteAttachment) | m.Post("/delete", repo.DeleteAttachment) | ||||
| m.Get("/get_pre_url", repo.GetPresignedPutObjectURL) | m.Get("/get_pre_url", repo.GetPresignedPutObjectURL) | ||||
| m.Post("/add", repo.AddAttachment) | m.Post("/add", repo.AddAttachment) | ||||
| m.Get("/upload", repo.UploadAttachmentUI) | |||||
| m.Post("/private", repo.UpdatePublicAttachment) | m.Post("/private", repo.UpdatePublicAttachment) | ||||
| m.Get("/get_chunks", repo.GetSuccessChunks) | m.Get("/get_chunks", repo.GetSuccessChunks) | ||||
| m.Get("/new_multipart", repo.NewMultipart) | m.Get("/new_multipart", repo.NewMultipart) | ||||
| @@ -978,7 +979,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/datasets", func() { | m.Group("/datasets", func() { | ||||
| m.Get("", reqRepoDatasetReader, repo.DatasetIndex) | m.Get("", reqRepoDatasetReader, repo.DatasetIndex) | ||||
| m.Post("", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost) | |||||
| m.Put("/:id/:action", reqRepoDatasetReader, repo.DatasetAction) | |||||
| m.Get("/create", reqRepoDatasetWriter, repo.CreateDataset) | |||||
| m.Post("/create", reqRepoDatasetWriter, bindIgnErr(auth.CreateDatasetForm{}), repo.CreateDatasetPost) | |||||
| m.Get("/edit/:id", reqRepoDatasetWriter, repo.EditDataset) | |||||
| m.Post("/edit", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost) | |||||
| m.Group("/dirs", func() { | m.Group("/dirs", func() { | ||||
| m.Get("/:uuid", reqRepoDatasetReader, repo.DirIndex) | m.Get("/:uuid", reqRepoDatasetReader, repo.DirIndex) | ||||