code.gitea.io/gitea@v1.22.3/modules/structs/issue.go (about) 1 // Copyright 2016 The Gogs Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package structs 5 6 import ( 7 "fmt" 8 "path" 9 "slices" 10 "strings" 11 "time" 12 13 "gopkg.in/yaml.v3" 14 ) 15 16 // StateType issue state type 17 type StateType string 18 19 const ( 20 // StateOpen pr is opend 21 StateOpen StateType = "open" 22 // StateClosed pr is closed 23 StateClosed StateType = "closed" 24 // StateAll is all 25 StateAll StateType = "all" 26 ) 27 28 // PullRequestMeta PR info if an issue is a PR 29 type PullRequestMeta struct { 30 HasMerged bool `json:"merged"` 31 Merged *time.Time `json:"merged_at"` 32 IsWorkInProgress bool `json:"draft"` 33 HTMLURL string `json:"html_url"` 34 } 35 36 // RepositoryMeta basic repository information 37 type RepositoryMeta struct { 38 ID int64 `json:"id"` 39 Name string `json:"name"` 40 Owner string `json:"owner"` 41 FullName string `json:"full_name"` 42 } 43 44 // Issue represents an issue in a repository 45 // swagger:model 46 type Issue struct { 47 ID int64 `json:"id"` 48 URL string `json:"url"` 49 HTMLURL string `json:"html_url"` 50 Index int64 `json:"number"` 51 Poster *User `json:"user"` 52 OriginalAuthor string `json:"original_author"` 53 OriginalAuthorID int64 `json:"original_author_id"` 54 Title string `json:"title"` 55 Body string `json:"body"` 56 Ref string `json:"ref"` 57 Attachments []*Attachment `json:"assets"` 58 Labels []*Label `json:"labels"` 59 Milestone *Milestone `json:"milestone"` 60 // deprecated 61 Assignee *User `json:"assignee"` 62 Assignees []*User `json:"assignees"` 63 // Whether the issue is open or closed 64 // 65 // type: string 66 // enum: open,closed 67 State StateType `json:"state"` 68 IsLocked bool `json:"is_locked"` 69 Comments int `json:"comments"` 70 // swagger:strfmt date-time 71 Created time.Time `json:"created_at"` 72 // swagger:strfmt date-time 73 Updated time.Time `json:"updated_at"` 74 // swagger:strfmt date-time 75 Closed *time.Time `json:"closed_at"` 76 // swagger:strfmt date-time 77 Deadline *time.Time `json:"due_date"` 78 79 PullRequest *PullRequestMeta `json:"pull_request"` 80 Repo *RepositoryMeta `json:"repository"` 81 82 PinOrder int `json:"pin_order"` 83 } 84 85 // CreateIssueOption options to create one issue 86 type CreateIssueOption struct { 87 // required:true 88 Title string `json:"title" binding:"Required"` 89 Body string `json:"body"` 90 Ref string `json:"ref"` 91 // deprecated 92 Assignee string `json:"assignee"` 93 Assignees []string `json:"assignees"` 94 // swagger:strfmt date-time 95 Deadline *time.Time `json:"due_date"` 96 // milestone id 97 Milestone int64 `json:"milestone"` 98 // list of label ids 99 Labels []int64 `json:"labels"` 100 Closed bool `json:"closed"` 101 } 102 103 // EditIssueOption options for editing an issue 104 type EditIssueOption struct { 105 Title string `json:"title"` 106 Body *string `json:"body"` 107 Ref *string `json:"ref"` 108 // deprecated 109 Assignee *string `json:"assignee"` 110 Assignees []string `json:"assignees"` 111 Milestone *int64 `json:"milestone"` 112 State *string `json:"state"` 113 // swagger:strfmt date-time 114 Deadline *time.Time `json:"due_date"` 115 RemoveDeadline *bool `json:"unset_due_date"` 116 } 117 118 // EditDeadlineOption options for creating a deadline 119 type EditDeadlineOption struct { 120 // required:true 121 // swagger:strfmt date-time 122 Deadline *time.Time `json:"due_date"` 123 } 124 125 // IssueDeadline represents an issue deadline 126 // swagger:model 127 type IssueDeadline struct { 128 // swagger:strfmt date-time 129 Deadline *time.Time `json:"due_date"` 130 } 131 132 // IssueFormFieldType defines issue form field type, can be "markdown", "textarea", "input", "dropdown" or "checkboxes" 133 type IssueFormFieldType string 134 135 const ( 136 IssueFormFieldTypeMarkdown IssueFormFieldType = "markdown" 137 IssueFormFieldTypeTextarea IssueFormFieldType = "textarea" 138 IssueFormFieldTypeInput IssueFormFieldType = "input" 139 IssueFormFieldTypeDropdown IssueFormFieldType = "dropdown" 140 IssueFormFieldTypeCheckboxes IssueFormFieldType = "checkboxes" 141 ) 142 143 // IssueFormField represents a form field 144 // swagger:model 145 type IssueFormField struct { 146 Type IssueFormFieldType `json:"type" yaml:"type"` 147 ID string `json:"id" yaml:"id"` 148 Attributes map[string]any `json:"attributes" yaml:"attributes"` 149 Validations map[string]any `json:"validations" yaml:"validations"` 150 Visible []IssueFormFieldVisible `json:"visible,omitempty"` 151 } 152 153 func (iff IssueFormField) VisibleOnForm() bool { 154 if len(iff.Visible) == 0 { 155 return true 156 } 157 return slices.Contains(iff.Visible, IssueFormFieldVisibleForm) 158 } 159 160 func (iff IssueFormField) VisibleInContent() bool { 161 if len(iff.Visible) == 0 { 162 // we have our markdown exception 163 return iff.Type != IssueFormFieldTypeMarkdown 164 } 165 return slices.Contains(iff.Visible, IssueFormFieldVisibleContent) 166 } 167 168 // IssueFormFieldVisible defines issue form field visible 169 // swagger:model 170 type IssueFormFieldVisible string 171 172 const ( 173 IssueFormFieldVisibleForm IssueFormFieldVisible = "form" 174 IssueFormFieldVisibleContent IssueFormFieldVisible = "content" 175 ) 176 177 // IssueTemplate represents an issue template for a repository 178 // swagger:model 179 type IssueTemplate struct { 180 Name string `json:"name" yaml:"name"` 181 Title string `json:"title" yaml:"title"` 182 About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible 183 Labels IssueTemplateLabels `json:"labels" yaml:"labels"` 184 Ref string `json:"ref" yaml:"ref"` 185 Content string `json:"content" yaml:"-"` 186 Fields []*IssueFormField `json:"body" yaml:"body"` 187 FileName string `json:"file_name" yaml:"-"` 188 } 189 190 type IssueTemplateLabels []string 191 192 func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error { 193 var labels []string 194 if value.IsZero() { 195 *l = labels 196 return nil 197 } 198 switch value.Kind { 199 case yaml.ScalarNode: 200 str := "" 201 err := value.Decode(&str) 202 if err != nil { 203 return err 204 } 205 for _, v := range strings.Split(str, ",") { 206 if v = strings.TrimSpace(v); v == "" { 207 continue 208 } 209 labels = append(labels, v) 210 } 211 *l = labels 212 return nil 213 case yaml.SequenceNode: 214 if err := value.Decode(&labels); err != nil { 215 return err 216 } 217 *l = labels 218 return nil 219 } 220 return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateLabels", value.Line, value.ShortTag()) 221 } 222 223 type IssueConfigContactLink struct { 224 Name string `json:"name" yaml:"name"` 225 URL string `json:"url" yaml:"url"` 226 About string `json:"about" yaml:"about"` 227 } 228 229 type IssueConfig struct { 230 BlankIssuesEnabled bool `json:"blank_issues_enabled" yaml:"blank_issues_enabled"` 231 ContactLinks []IssueConfigContactLink `json:"contact_links" yaml:"contact_links"` 232 } 233 234 type IssueConfigValidation struct { 235 Valid bool `json:"valid"` 236 Message string `json:"message"` 237 } 238 239 // IssueTemplateType defines issue template type 240 type IssueTemplateType string 241 242 const ( 243 IssueTemplateTypeMarkdown IssueTemplateType = "md" 244 IssueTemplateTypeYaml IssueTemplateType = "yaml" 245 ) 246 247 // Type returns the type of IssueTemplate, can be "md", "yaml" or empty for known 248 func (it IssueTemplate) Type() IssueTemplateType { 249 if base := path.Base(it.FileName); base == "config.yaml" || base == "config.yml" { 250 // ignore config.yaml which is a special configuration file 251 return "" 252 } 253 if ext := path.Ext(it.FileName); ext == ".md" { 254 return IssueTemplateTypeMarkdown 255 } else if ext == ".yaml" || ext == ".yml" { 256 return IssueTemplateTypeYaml 257 } 258 return "" 259 } 260 261 // IssueMeta basic issue information 262 // swagger:model 263 type IssueMeta struct { 264 Index int64 `json:"index"` 265 Owner string `json:"owner"` 266 Name string `json:"repo"` 267 }