code.gitea.io/gitea@v1.22.3/routers/api/v1/org/org.go (about) 1 // Copyright 2015 The Gogs Authors. All rights reserved. 2 // Copyright 2018 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package org 6 7 import ( 8 "net/http" 9 10 activities_model "code.gitea.io/gitea/models/activities" 11 "code.gitea.io/gitea/models/db" 12 "code.gitea.io/gitea/models/organization" 13 "code.gitea.io/gitea/models/perm" 14 user_model "code.gitea.io/gitea/models/user" 15 "code.gitea.io/gitea/modules/optional" 16 api "code.gitea.io/gitea/modules/structs" 17 "code.gitea.io/gitea/modules/web" 18 "code.gitea.io/gitea/routers/api/v1/user" 19 "code.gitea.io/gitea/routers/api/v1/utils" 20 "code.gitea.io/gitea/services/context" 21 "code.gitea.io/gitea/services/convert" 22 "code.gitea.io/gitea/services/org" 23 user_service "code.gitea.io/gitea/services/user" 24 ) 25 26 func listUserOrgs(ctx *context.APIContext, u *user_model.User) { 27 listOptions := utils.GetListOptions(ctx) 28 showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == u.ID) 29 30 opts := organization.FindOrgOptions{ 31 ListOptions: listOptions, 32 UserID: u.ID, 33 IncludePrivate: showPrivate, 34 } 35 orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts) 36 if err != nil { 37 ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err) 38 return 39 } 40 41 apiOrgs := make([]*api.Organization, len(orgs)) 42 for i := range orgs { 43 apiOrgs[i] = convert.ToOrganization(ctx, orgs[i]) 44 } 45 46 ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) 47 ctx.SetTotalCountHeader(maxResults) 48 ctx.JSON(http.StatusOK, &apiOrgs) 49 } 50 51 // ListMyOrgs list all my orgs 52 func ListMyOrgs(ctx *context.APIContext) { 53 // swagger:operation GET /user/orgs organization orgListCurrentUserOrgs 54 // --- 55 // summary: List the current user's organizations 56 // produces: 57 // - application/json 58 // parameters: 59 // - name: page 60 // in: query 61 // description: page number of results to return (1-based) 62 // type: integer 63 // - name: limit 64 // in: query 65 // description: page size of results 66 // type: integer 67 // responses: 68 // "200": 69 // "$ref": "#/responses/OrganizationList" 70 // "404": 71 // "$ref": "#/responses/notFound" 72 73 listUserOrgs(ctx, ctx.Doer) 74 } 75 76 // ListUserOrgs list user's orgs 77 func ListUserOrgs(ctx *context.APIContext) { 78 // swagger:operation GET /users/{username}/orgs organization orgListUserOrgs 79 // --- 80 // summary: List a user's organizations 81 // produces: 82 // - application/json 83 // parameters: 84 // - name: username 85 // in: path 86 // description: username of user 87 // type: string 88 // required: true 89 // - name: page 90 // in: query 91 // description: page number of results to return (1-based) 92 // type: integer 93 // - name: limit 94 // in: query 95 // description: page size of results 96 // type: integer 97 // responses: 98 // "200": 99 // "$ref": "#/responses/OrganizationList" 100 // "404": 101 // "$ref": "#/responses/notFound" 102 103 listUserOrgs(ctx, ctx.ContextUser) 104 } 105 106 // GetUserOrgsPermissions get user permissions in organization 107 func GetUserOrgsPermissions(ctx *context.APIContext) { 108 // swagger:operation GET /users/{username}/orgs/{org}/permissions organization orgGetUserPermissions 109 // --- 110 // summary: Get user permissions in organization 111 // produces: 112 // - application/json 113 // parameters: 114 // - name: username 115 // in: path 116 // description: username of user 117 // type: string 118 // required: true 119 // - name: org 120 // in: path 121 // description: name of the organization 122 // type: string 123 // required: true 124 // responses: 125 // "200": 126 // "$ref": "#/responses/OrganizationPermissions" 127 // "403": 128 // "$ref": "#/responses/forbidden" 129 // "404": 130 // "$ref": "#/responses/notFound" 131 132 var o *user_model.User 133 if o = user.GetUserByParamsName(ctx, ":org"); o == nil { 134 return 135 } 136 137 op := api.OrganizationPermissions{} 138 139 if !organization.HasOrgOrUserVisible(ctx, o, ctx.ContextUser) { 140 ctx.NotFound("HasOrgOrUserVisible", nil) 141 return 142 } 143 144 org := organization.OrgFromUser(o) 145 authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx, ctx.ContextUser.ID) 146 if err != nil { 147 ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err) 148 return 149 } 150 151 if authorizeLevel > perm.AccessModeNone { 152 op.CanRead = true 153 } 154 if authorizeLevel > perm.AccessModeRead { 155 op.CanWrite = true 156 } 157 if authorizeLevel > perm.AccessModeWrite { 158 op.IsAdmin = true 159 } 160 if authorizeLevel > perm.AccessModeAdmin { 161 op.IsOwner = true 162 } 163 164 op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx, ctx.ContextUser.ID) 165 if err != nil { 166 ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err) 167 return 168 } 169 170 ctx.JSON(http.StatusOK, op) 171 } 172 173 // GetAll return list of all public organizations 174 func GetAll(ctx *context.APIContext) { 175 // swagger:operation Get /orgs organization orgGetAll 176 // --- 177 // summary: Get list of organizations 178 // produces: 179 // - application/json 180 // parameters: 181 // - name: page 182 // in: query 183 // description: page number of results to return (1-based) 184 // type: integer 185 // - name: limit 186 // in: query 187 // description: page size of results 188 // type: integer 189 // responses: 190 // "200": 191 // "$ref": "#/responses/OrganizationList" 192 193 vMode := []api.VisibleType{api.VisibleTypePublic} 194 if ctx.IsSigned && !ctx.PublicOnly { 195 vMode = append(vMode, api.VisibleTypeLimited) 196 if ctx.Doer.IsAdmin { 197 vMode = append(vMode, api.VisibleTypePrivate) 198 } 199 } 200 201 listOptions := utils.GetListOptions(ctx) 202 203 publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ 204 Actor: ctx.Doer, 205 ListOptions: listOptions, 206 Type: user_model.UserTypeOrganization, 207 OrderBy: db.SearchOrderByAlphabetically, 208 Visible: vMode, 209 }) 210 if err != nil { 211 ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err) 212 return 213 } 214 orgs := make([]*api.Organization, len(publicOrgs)) 215 for i := range publicOrgs { 216 orgs[i] = convert.ToOrganization(ctx, organization.OrgFromUser(publicOrgs[i])) 217 } 218 219 ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) 220 ctx.SetTotalCountHeader(maxResults) 221 ctx.JSON(http.StatusOK, &orgs) 222 } 223 224 // Create api for create organization 225 func Create(ctx *context.APIContext) { 226 // swagger:operation POST /orgs organization orgCreate 227 // --- 228 // summary: Create an organization 229 // consumes: 230 // - application/json 231 // produces: 232 // - application/json 233 // parameters: 234 // - name: organization 235 // in: body 236 // required: true 237 // schema: { "$ref": "#/definitions/CreateOrgOption" } 238 // responses: 239 // "201": 240 // "$ref": "#/responses/Organization" 241 // "403": 242 // "$ref": "#/responses/forbidden" 243 // "422": 244 // "$ref": "#/responses/validationError" 245 form := web.GetForm(ctx).(*api.CreateOrgOption) 246 if !ctx.Doer.CanCreateOrganization() { 247 ctx.Error(http.StatusForbidden, "Create organization not allowed", nil) 248 return 249 } 250 251 visibility := api.VisibleTypePublic 252 if form.Visibility != "" { 253 visibility = api.VisibilityModes[form.Visibility] 254 } 255 256 org := &organization.Organization{ 257 Name: form.UserName, 258 FullName: form.FullName, 259 Email: form.Email, 260 Description: form.Description, 261 Website: form.Website, 262 Location: form.Location, 263 IsActive: true, 264 Type: user_model.UserTypeOrganization, 265 Visibility: visibility, 266 RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess, 267 } 268 if err := organization.CreateOrganization(ctx, org, ctx.Doer); err != nil { 269 if user_model.IsErrUserAlreadyExist(err) || 270 db.IsErrNameReserved(err) || 271 db.IsErrNameCharsNotAllowed(err) || 272 db.IsErrNamePatternNotAllowed(err) { 273 ctx.Error(http.StatusUnprocessableEntity, "", err) 274 } else { 275 ctx.Error(http.StatusInternalServerError, "CreateOrganization", err) 276 } 277 return 278 } 279 280 ctx.JSON(http.StatusCreated, convert.ToOrganization(ctx, org)) 281 } 282 283 // Get get an organization 284 func Get(ctx *context.APIContext) { 285 // swagger:operation GET /orgs/{org} organization orgGet 286 // --- 287 // summary: Get an organization 288 // produces: 289 // - application/json 290 // parameters: 291 // - name: org 292 // in: path 293 // description: name of the organization to get 294 // type: string 295 // required: true 296 // responses: 297 // "200": 298 // "$ref": "#/responses/Organization" 299 // "404": 300 // "$ref": "#/responses/notFound" 301 302 if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) { 303 ctx.NotFound("HasOrgOrUserVisible", nil) 304 return 305 } 306 307 org := convert.ToOrganization(ctx, ctx.Org.Organization) 308 309 // Don't show Mail, when User is not logged in 310 if ctx.Doer == nil { 311 org.Email = "" 312 } 313 314 ctx.JSON(http.StatusOK, org) 315 } 316 317 // Edit change an organization's information 318 func Edit(ctx *context.APIContext) { 319 // swagger:operation PATCH /orgs/{org} organization orgEdit 320 // --- 321 // summary: Edit an organization 322 // consumes: 323 // - application/json 324 // produces: 325 // - application/json 326 // parameters: 327 // - name: org 328 // in: path 329 // description: name of the organization to edit 330 // type: string 331 // required: true 332 // - name: body 333 // in: body 334 // required: true 335 // schema: 336 // "$ref": "#/definitions/EditOrgOption" 337 // responses: 338 // "200": 339 // "$ref": "#/responses/Organization" 340 // "404": 341 // "$ref": "#/responses/notFound" 342 343 form := web.GetForm(ctx).(*api.EditOrgOption) 344 345 if form.Email != "" { 346 if err := user_service.ReplacePrimaryEmailAddress(ctx, ctx.Org.Organization.AsUser(), form.Email); err != nil { 347 ctx.Error(http.StatusInternalServerError, "ReplacePrimaryEmailAddress", err) 348 return 349 } 350 } 351 352 opts := &user_service.UpdateOptions{ 353 FullName: optional.Some(form.FullName), 354 Description: optional.Some(form.Description), 355 Website: optional.Some(form.Website), 356 Location: optional.Some(form.Location), 357 Visibility: optional.FromNonDefault(api.VisibilityModes[form.Visibility]), 358 RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess), 359 } 360 if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil { 361 ctx.Error(http.StatusInternalServerError, "UpdateUser", err) 362 return 363 } 364 365 ctx.JSON(http.StatusOK, convert.ToOrganization(ctx, ctx.Org.Organization)) 366 } 367 368 // Delete an organization 369 func Delete(ctx *context.APIContext) { 370 // swagger:operation DELETE /orgs/{org} organization orgDelete 371 // --- 372 // summary: Delete an organization 373 // produces: 374 // - application/json 375 // parameters: 376 // - name: org 377 // in: path 378 // description: organization that is to be deleted 379 // type: string 380 // required: true 381 // responses: 382 // "204": 383 // "$ref": "#/responses/empty" 384 // "404": 385 // "$ref": "#/responses/notFound" 386 387 if err := org.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil { 388 ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err) 389 return 390 } 391 ctx.Status(http.StatusNoContent) 392 } 393 394 func ListOrgActivityFeeds(ctx *context.APIContext) { 395 // swagger:operation GET /orgs/{org}/activities/feeds organization orgListActivityFeeds 396 // --- 397 // summary: List an organization's activity feeds 398 // produces: 399 // - application/json 400 // parameters: 401 // - name: org 402 // in: path 403 // description: name of the org 404 // type: string 405 // required: true 406 // - name: date 407 // in: query 408 // description: the date of the activities to be found 409 // type: string 410 // format: date 411 // - name: page 412 // in: query 413 // description: page number of results to return (1-based) 414 // type: integer 415 // - name: limit 416 // in: query 417 // description: page size of results 418 // type: integer 419 // responses: 420 // "200": 421 // "$ref": "#/responses/ActivityFeedsList" 422 // "404": 423 // "$ref": "#/responses/notFound" 424 425 includePrivate := false 426 if ctx.IsSigned { 427 if ctx.Doer.IsAdmin { 428 includePrivate = true 429 } else { 430 org := organization.OrgFromUser(ctx.ContextUser) 431 isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID) 432 if err != nil { 433 ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) 434 return 435 } 436 includePrivate = isMember 437 } 438 } 439 440 listOptions := utils.GetListOptions(ctx) 441 442 opts := activities_model.GetFeedsOptions{ 443 RequestedUser: ctx.ContextUser, 444 Actor: ctx.Doer, 445 IncludePrivate: includePrivate, 446 Date: ctx.FormString("date"), 447 ListOptions: listOptions, 448 } 449 450 feeds, count, err := activities_model.GetFeeds(ctx, opts) 451 if err != nil { 452 ctx.Error(http.StatusInternalServerError, "GetFeeds", err) 453 return 454 } 455 ctx.SetTotalCountHeader(count) 456 457 ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer)) 458 }