1// Copyright 2017 The Gitea Authors. All rights reserved. 2// Use of this source code is governed by a MIT-style 3// license that can be found in the LICENSE file. 4 5package unit 6 7import ( 8 "fmt" 9 "strings" 10 11 "code.gitea.io/gitea/models/perm" 12 "code.gitea.io/gitea/modules/log" 13 "code.gitea.io/gitea/modules/setting" 14) 15 16// Type is Unit's Type 17type Type int 18 19// Enumerate all the unit types 20const ( 21 TypeInvalid Type = iota // 0 invalid 22 TypeCode // 1 code 23 TypeIssues // 2 issues 24 TypePullRequests // 3 PRs 25 TypeReleases // 4 Releases 26 TypeWiki // 5 Wiki 27 TypeExternalWiki // 6 ExternalWiki 28 TypeExternalTracker // 7 ExternalTracker 29 TypeProjects // 8 Kanban board 30) 31 32// Value returns integer value for unit type 33func (u Type) Value() int { 34 return int(u) 35} 36 37func (u Type) String() string { 38 switch u { 39 case TypeCode: 40 return "TypeCode" 41 case TypeIssues: 42 return "TypeIssues" 43 case TypePullRequests: 44 return "TypePullRequests" 45 case TypeReleases: 46 return "TypeReleases" 47 case TypeWiki: 48 return "TypeWiki" 49 case TypeExternalWiki: 50 return "TypeExternalWiki" 51 case TypeExternalTracker: 52 return "TypeExternalTracker" 53 case TypeProjects: 54 return "TypeProjects" 55 } 56 return fmt.Sprintf("Unknown Type %d", u) 57} 58 59// ColorFormat provides a ColorFormatted version of this Type 60func (u Type) ColorFormat(s fmt.State) { 61 log.ColorFprintf(s, "%d:%s", 62 log.NewColoredIDValue(u), 63 u) 64} 65 66var ( 67 // AllRepoUnitTypes contains all the unit types 68 AllRepoUnitTypes = []Type{ 69 TypeCode, 70 TypeIssues, 71 TypePullRequests, 72 TypeReleases, 73 TypeWiki, 74 TypeExternalWiki, 75 TypeExternalTracker, 76 TypeProjects, 77 } 78 79 // DefaultRepoUnits contains the default unit types 80 DefaultRepoUnits = []Type{ 81 TypeCode, 82 TypeIssues, 83 TypePullRequests, 84 TypeReleases, 85 TypeWiki, 86 TypeProjects, 87 } 88 89 // NotAllowedDefaultRepoUnits contains units that can't be default 90 NotAllowedDefaultRepoUnits = []Type{ 91 TypeExternalWiki, 92 TypeExternalTracker, 93 } 94 95 // MustRepoUnits contains the units could not be disabled currently 96 MustRepoUnits = []Type{ 97 TypeCode, 98 TypeReleases, 99 } 100 101 // DisabledRepoUnits contains the units that have been globally disabled 102 DisabledRepoUnits = []Type{} 103) 104 105// LoadUnitConfig load units from settings 106func LoadUnitConfig() { 107 setDefaultRepoUnits := FindUnitTypes(setting.Repository.DefaultRepoUnits...) 108 // Default repo units set if setting is not empty 109 if len(setDefaultRepoUnits) > 0 { 110 // MustRepoUnits required as default 111 DefaultRepoUnits = make([]Type, len(MustRepoUnits)) 112 copy(DefaultRepoUnits, MustRepoUnits) 113 for _, defaultU := range setDefaultRepoUnits { 114 if !defaultU.CanBeDefault() { 115 log.Warn("Not allowed as default unit: %s", defaultU.String()) 116 continue 117 } 118 // MustRepoUnits already added 119 if defaultU.CanDisable() { 120 DefaultRepoUnits = append(DefaultRepoUnits, defaultU) 121 } 122 } 123 } 124 125 DisabledRepoUnits = FindUnitTypes(setting.Repository.DisabledRepoUnits...) 126 // Check that must units are not disabled 127 for i, disabledU := range DisabledRepoUnits { 128 if !disabledU.CanDisable() { 129 log.Warn("Not allowed to global disable unit %s", disabledU.String()) 130 DisabledRepoUnits = append(DisabledRepoUnits[:i], DisabledRepoUnits[i+1:]...) 131 } 132 } 133 // Remove disabled units from default units 134 for _, disabledU := range DisabledRepoUnits { 135 for i, defaultU := range DefaultRepoUnits { 136 if defaultU == disabledU { 137 DefaultRepoUnits = append(DefaultRepoUnits[:i], DefaultRepoUnits[i+1:]...) 138 } 139 } 140 } 141} 142 143// UnitGlobalDisabled checks if unit type is global disabled 144func (u Type) UnitGlobalDisabled() bool { 145 for _, ud := range DisabledRepoUnits { 146 if u == ud { 147 return true 148 } 149 } 150 return false 151} 152 153// CanDisable checks if this unit type can be disabled. 154func (u *Type) CanDisable() bool { 155 for _, mu := range MustRepoUnits { 156 if *u == mu { 157 return false 158 } 159 } 160 return true 161} 162 163// CanBeDefault checks if the unit type can be a default repo unit 164func (u *Type) CanBeDefault() bool { 165 for _, nadU := range NotAllowedDefaultRepoUnits { 166 if *u == nadU { 167 return false 168 } 169 } 170 return true 171} 172 173// Unit is a section of one repository 174type Unit struct { 175 Type Type 176 NameKey string 177 URI string 178 DescKey string 179 Idx int 180 MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read. 181} 182 183// CanDisable returns if this unit could be disabled. 184func (u *Unit) CanDisable() bool { 185 return u.Type.CanDisable() 186} 187 188// IsLessThan compares order of two units 189func (u Unit) IsLessThan(unit Unit) bool { 190 if (u.Type == TypeExternalTracker || u.Type == TypeExternalWiki) && unit.Type != TypeExternalTracker && unit.Type != TypeExternalWiki { 191 return false 192 } 193 return u.Idx < unit.Idx 194} 195 196// MaxPerm returns the max perms of this unit 197func (u Unit) MaxPerm() perm.AccessMode { 198 if u.Type == TypeExternalTracker || u.Type == TypeExternalWiki { 199 return perm.AccessModeRead 200 } 201 return perm.AccessModeAdmin 202} 203 204// Enumerate all the units 205var ( 206 UnitCode = Unit{ 207 TypeCode, 208 "repo.code", 209 "/", 210 "repo.code.desc", 211 0, 212 perm.AccessModeOwner, 213 } 214 215 UnitIssues = Unit{ 216 TypeIssues, 217 "repo.issues", 218 "/issues", 219 "repo.issues.desc", 220 1, 221 perm.AccessModeOwner, 222 } 223 224 UnitExternalTracker = Unit{ 225 TypeExternalTracker, 226 "repo.ext_issues", 227 "/issues", 228 "repo.ext_issues.desc", 229 1, 230 perm.AccessModeRead, 231 } 232 233 UnitPullRequests = Unit{ 234 TypePullRequests, 235 "repo.pulls", 236 "/pulls", 237 "repo.pulls.desc", 238 2, 239 perm.AccessModeOwner, 240 } 241 242 UnitReleases = Unit{ 243 TypeReleases, 244 "repo.releases", 245 "/releases", 246 "repo.releases.desc", 247 3, 248 perm.AccessModeOwner, 249 } 250 251 UnitWiki = Unit{ 252 TypeWiki, 253 "repo.wiki", 254 "/wiki", 255 "repo.wiki.desc", 256 4, 257 perm.AccessModeOwner, 258 } 259 260 UnitExternalWiki = Unit{ 261 TypeExternalWiki, 262 "repo.ext_wiki", 263 "/wiki", 264 "repo.ext_wiki.desc", 265 4, 266 perm.AccessModeRead, 267 } 268 269 UnitProjects = Unit{ 270 TypeProjects, 271 "repo.projects", 272 "/projects", 273 "repo.projects.desc", 274 5, 275 perm.AccessModeOwner, 276 } 277 278 // Units contains all the units 279 Units = map[Type]Unit{ 280 TypeCode: UnitCode, 281 TypeIssues: UnitIssues, 282 TypeExternalTracker: UnitExternalTracker, 283 TypePullRequests: UnitPullRequests, 284 TypeReleases: UnitReleases, 285 TypeWiki: UnitWiki, 286 TypeExternalWiki: UnitExternalWiki, 287 TypeProjects: UnitProjects, 288 } 289) 290 291// FindUnitTypes give the unit key names and return unit 292func FindUnitTypes(nameKeys ...string) (res []Type) { 293 for _, key := range nameKeys { 294 var found bool 295 for t, u := range Units { 296 if strings.EqualFold(key, u.NameKey) { 297 res = append(res, t) 298 found = true 299 break 300 } 301 } 302 if !found { 303 res = append(res, TypeInvalid) 304 } 305 } 306 return 307} 308 309// TypeFromKey give the unit key name and return unit 310func TypeFromKey(nameKey string) Type { 311 for t, u := range Units { 312 if strings.EqualFold(nameKey, u.NameKey) { 313 return t 314 } 315 } 316 return TypeInvalid 317} 318 319// AllUnitKeyNames returns all unit key names 320func AllUnitKeyNames() []string { 321 res := make([]string, 0, len(Units)) 322 for _, u := range Units { 323 res = append(res, u.NameKey) 324 } 325 return res 326} 327 328// MinUnitAccessMode returns the minial permission of the permission map 329func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode { 330 res := perm.AccessModeNone 331 for t, mode := range unitsMap { 332 // Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms. 333 if t == TypeExternalTracker || t == TypeExternalWiki { 334 continue 335 } 336 337 // get the minial permission great than AccessModeNone except all are AccessModeNone 338 if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) { 339 res = mode 340 } 341 } 342 return res 343} 344