1// Copyright 2015 The Gogs Authors. All rights reserved.
2// Copyright 2020 The Gitea Authors.
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file.
5
6package user
7
8import (
9	"net/http"
10
11	user_model "code.gitea.io/gitea/models/user"
12	"code.gitea.io/gitea/modules/context"
13	"code.gitea.io/gitea/modules/convert"
14	api "code.gitea.io/gitea/modules/structs"
15	"code.gitea.io/gitea/routers/api/v1/utils"
16)
17
18func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
19	apiUsers := make([]*api.User, len(users))
20	for i := range users {
21		apiUsers[i] = convert.ToUser(users[i], ctx.User)
22	}
23	ctx.JSON(http.StatusOK, &apiUsers)
24}
25
26func listUserFollowers(ctx *context.APIContext, u *user_model.User) {
27	users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx))
28	if err != nil {
29		ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
30		return
31	}
32
33	ctx.SetTotalCountHeader(int64(u.NumFollowers))
34	responseAPIUsers(ctx, users)
35}
36
37// ListMyFollowers list the authenticated user's followers
38func ListMyFollowers(ctx *context.APIContext) {
39	// swagger:operation GET /user/followers user userCurrentListFollowers
40	// ---
41	// summary: List the authenticated user's followers
42	// parameters:
43	// - name: page
44	//   in: query
45	//   description: page number of results to return (1-based)
46	//   type: integer
47	// - name: limit
48	//   in: query
49	//   description: page size of results
50	//   type: integer
51	// produces:
52	// - application/json
53	// responses:
54	//   "200":
55	//     "$ref": "#/responses/UserList"
56
57	listUserFollowers(ctx, ctx.User)
58}
59
60// ListFollowers list the given user's followers
61func ListFollowers(ctx *context.APIContext) {
62	// swagger:operation GET /users/{username}/followers user userListFollowers
63	// ---
64	// summary: List the given user's followers
65	// produces:
66	// - application/json
67	// parameters:
68	// - name: username
69	//   in: path
70	//   description: username of user
71	//   type: string
72	//   required: true
73	// - name: page
74	//   in: query
75	//   description: page number of results to return (1-based)
76	//   type: integer
77	// - name: limit
78	//   in: query
79	//   description: page size of results
80	//   type: integer
81	// responses:
82	//   "200":
83	//     "$ref": "#/responses/UserList"
84
85	u := GetUserByParams(ctx)
86	if ctx.Written() {
87		return
88	}
89	listUserFollowers(ctx, u)
90}
91
92func listUserFollowing(ctx *context.APIContext, u *user_model.User) {
93	users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx))
94	if err != nil {
95		ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
96		return
97	}
98
99	ctx.SetTotalCountHeader(int64(u.NumFollowing))
100	responseAPIUsers(ctx, users)
101}
102
103// ListMyFollowing list the users that the authenticated user is following
104func ListMyFollowing(ctx *context.APIContext) {
105	// swagger:operation GET /user/following user userCurrentListFollowing
106	// ---
107	// summary: List the users that the authenticated user is following
108	// parameters:
109	// - name: page
110	//   in: query
111	//   description: page number of results to return (1-based)
112	//   type: integer
113	// - name: limit
114	//   in: query
115	//   description: page size of results
116	//   type: integer
117	// produces:
118	// - application/json
119	// responses:
120	//   "200":
121	//     "$ref": "#/responses/UserList"
122
123	listUserFollowing(ctx, ctx.User)
124}
125
126// ListFollowing list the users that the given user is following
127func ListFollowing(ctx *context.APIContext) {
128	// swagger:operation GET /users/{username}/following user userListFollowing
129	// ---
130	// summary: List the users that the given user is following
131	// produces:
132	// - application/json
133	// parameters:
134	// - name: username
135	//   in: path
136	//   description: username of user
137	//   type: string
138	//   required: true
139	// - name: page
140	//   in: query
141	//   description: page number of results to return (1-based)
142	//   type: integer
143	// - name: limit
144	//   in: query
145	//   description: page size of results
146	//   type: integer
147	// responses:
148	//   "200":
149	//     "$ref": "#/responses/UserList"
150
151	u := GetUserByParams(ctx)
152	if ctx.Written() {
153		return
154	}
155	listUserFollowing(ctx, u)
156}
157
158func checkUserFollowing(ctx *context.APIContext, u *user_model.User, followID int64) {
159	if user_model.IsFollowing(u.ID, followID) {
160		ctx.Status(http.StatusNoContent)
161	} else {
162		ctx.NotFound()
163	}
164}
165
166// CheckMyFollowing whether the given user is followed by the authenticated user
167func CheckMyFollowing(ctx *context.APIContext) {
168	// swagger:operation GET /user/following/{username} user userCurrentCheckFollowing
169	// ---
170	// summary: Check whether a user is followed by the authenticated user
171	// parameters:
172	// - name: username
173	//   in: path
174	//   description: username of followed user
175	//   type: string
176	//   required: true
177	// responses:
178	//   "204":
179	//     "$ref": "#/responses/empty"
180	//   "404":
181	//     "$ref": "#/responses/notFound"
182
183	target := GetUserByParams(ctx)
184	if ctx.Written() {
185		return
186	}
187	checkUserFollowing(ctx, ctx.User, target.ID)
188}
189
190// CheckFollowing check if one user is following another user
191func CheckFollowing(ctx *context.APIContext) {
192	// swagger:operation GET /users/{follower}/following/{followee} user userCheckFollowing
193	// ---
194	// summary: Check if one user is following another user
195	// parameters:
196	// - name: follower
197	//   in: path
198	//   description: username of following user
199	//   type: string
200	//   required: true
201	// - name: followee
202	//   in: path
203	//   description: username of followed user
204	//   type: string
205	//   required: true
206	// responses:
207	//   "204":
208	//     "$ref": "#/responses/empty"
209	//   "404":
210	//     "$ref": "#/responses/notFound"
211
212	u := GetUserByParams(ctx)
213	if ctx.Written() {
214		return
215	}
216	target := GetUserByParamsName(ctx, ":target")
217	if ctx.Written() {
218		return
219	}
220	checkUserFollowing(ctx, u, target.ID)
221}
222
223// Follow follow a user
224func Follow(ctx *context.APIContext) {
225	// swagger:operation PUT /user/following/{username} user userCurrentPutFollow
226	// ---
227	// summary: Follow a user
228	// parameters:
229	// - name: username
230	//   in: path
231	//   description: username of user to follow
232	//   type: string
233	//   required: true
234	// responses:
235	//   "204":
236	//     "$ref": "#/responses/empty"
237
238	target := GetUserByParams(ctx)
239	if ctx.Written() {
240		return
241	}
242	if err := user_model.FollowUser(ctx.User.ID, target.ID); err != nil {
243		ctx.Error(http.StatusInternalServerError, "FollowUser", err)
244		return
245	}
246	ctx.Status(http.StatusNoContent)
247}
248
249// Unfollow unfollow a user
250func Unfollow(ctx *context.APIContext) {
251	// swagger:operation DELETE /user/following/{username} user userCurrentDeleteFollow
252	// ---
253	// summary: Unfollow a user
254	// parameters:
255	// - name: username
256	//   in: path
257	//   description: username of user to unfollow
258	//   type: string
259	//   required: true
260	// responses:
261	//   "204":
262	//     "$ref": "#/responses/empty"
263
264	target := GetUserByParams(ctx)
265	if ctx.Written() {
266		return
267	}
268	if err := user_model.UnfollowUser(ctx.User.ID, target.ID); err != nil {
269		ctx.Error(http.StatusInternalServerError, "UnfollowUser", err)
270		return
271	}
272	ctx.Status(http.StatusNoContent)
273}
274