##// END OF EJS Templates
return proper id from users_group...
marcink -
r2531:d80a68e2 beta
parent child Browse files
Show More
@@ -1,764 +1,825 b''
1 .. _api:
1 .. _api:
2
2
3 ===
3 ===
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's a single schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request in RhodeCode is
10 with JSON protocol both ways. An url to send API request to RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13 API ACCESS FOR WEB VIEWS
13 API ACCESS FOR WEB VIEWS
14 ++++++++++++++++++++++++
14 ++++++++++++++++++++++++
15
15
16 API access can also be turned on for each web view in RhodeCode that is
16 API access can also be turned on for each web view in RhodeCode that is
17 decorated with `@LoginRequired` decorator. To enable API access simple change
17 decorated with `@LoginRequired` decorator. To enable API access simple change
18 the standard login decorator to `@LoginRequired(api_access=True)`.
18 the standard login decorator to `@LoginRequired(api_access=True)`.
19 After this change, a rhodecode view can be accessed without login by adding a
19 After this change, a rhodecode view can be accessed without login by adding a
20 GET parameter `?api_key=<api_key>` to url. By default this is only
20 GET parameter `?api_key=<api_key>` to url. By default this is only
21 enabled on RSS/ATOM feed views.
21 enabled on RSS/ATOM feed views.
22
22
23
23
24 API ACCESS
24 API ACCESS
25 ++++++++++
25 ++++++++++
26
26
27 All clients are required to send JSON-RPC spec JSON data::
27 All clients are required to send JSON-RPC spec JSON data::
28
28
29 {
29 {
30 "id:"<id>",
30 "id:"<id>",
31 "api_key":"<api_key>",
31 "api_key":"<api_key>",
32 "method":"<method_name>",
32 "method":"<method_name>",
33 "args":{"<arg_key>":"<arg_val>"}
33 "args":{"<arg_key>":"<arg_val>"}
34 }
34 }
35
35
36 Example call for autopulling remotes repos using curl::
36 Example call for autopulling remotes repos using curl::
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
38
38
39 Simply provide
39 Simply provide
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
41 - *api_key* for access and permission validation.
41 - *api_key* for access and permission validation.
42 - *method* is name of method to call
42 - *method* is name of method to call
43 - *args* is an key:value list of arguments to pass to method
43 - *args* is an key:value list of arguments to pass to method
44
44
45 .. note::
45 .. note::
46
46
47 api_key can be found in your user account page
47 api_key can be found in your user account page
48
48
49
49
50 RhodeCode API will return always a JSON-RPC response::
50 RhodeCode API will return always a JSON-RPC response::
51
51
52 {
52 {
53 "id":<id>, # matching id sent by request
53 "id":<id>, # matching id sent by request
54 "result": "<result>"|null, # JSON formatted result, null if any errors
54 "result": "<result>"|null, # JSON formatted result, null if any errors
55 "error": "null"|<error_message> # JSON formatted error (if any)
55 "error": "null"|<error_message> # JSON formatted error (if any)
56 }
56 }
57
57
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
59 calling api *error* key from response will contain failure description
59 calling api *error* key from response will contain failure description
60 and result will be null.
60 and result will be null.
61
61
62
62
63 API CLIENT
63 API CLIENT
64 ++++++++++
64 ++++++++++
65
65
66 From version 1.4 RhodeCode adds a binary script that allows to easily
66 From version 1.4 RhodeCode adds a script that allows to easily
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
68 will be available.
68 will be available.
69
69
70 To get started quickly simply run::
70 To get started quickly simply run::
71
71
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
73
73
74 This will create a file named .config in the directory you executed it storing
74 This will create a file named .config in the directory you executed it storing
75 json config file with credentials. You can skip this step and always provide
75 json config file with credentials. You can skip this step and always provide
76 both of the arguments to be able to communicate with server
76 both of the arguments to be able to communicate with server
77
77
78
78
79 after that simply run any api command for example get_repo::
79 after that simply run any api command for example get_repo::
80
80
81 rhodecode-api get_repo
81 rhodecode-api get_repo
82
82
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
84 rhodecode said:
84 rhodecode said:
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
86 'id': 75,
86 'id': 75,
87 'result': None}
87 'result': None}
88
88
89 Ups looks like we forgot to add an argument
89 Ups looks like we forgot to add an argument
90
90
91 Let's try again now giving the repoid as parameters::
91 Let's try again now giving the repoid as parameters::
92
92
93 rhodecode-api get_repo repoid:rhodecode
93 rhodecode-api get_repo repoid:rhodecode
94
94
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
96 rhodecode said:
96 rhodecode said:
97 {'error': None,
97 {'error': None,
98 'id': 39,
98 'id': 39,
99 'result': <json data...>}
99 'result': <json data...>}
100
100
101
101
102
102
103 API METHODS
103 API METHODS
104 +++++++++++
104 +++++++++++
105
105
106
106
107 pull
107 pull
108 ----
108 ----
109
109
110 Pulls given repo from remote location. Can be used to automatically keep
110 Pulls given repo from remote location. Can be used to automatically keep
111 remote repos up to date. This command can be executed only using api_key
111 remote repos up to date. This command can be executed only using api_key
112 belonging to user with admin rights
112 belonging to user with admin rights
113
113
114 INPUT::
114 INPUT::
115
115
116 id : <id_for_response>
116 id : <id_for_response>
117 api_key : "<api_key>"
117 api_key : "<api_key>"
118 method : "pull"
118 method : "pull"
119 args : {
119 args : {
120 "repo_name" : "<reponame>"
120 "repoid" : "<reponame or repo_id>"
121 }
121 }
122
122
123 OUTPUT::
123 OUTPUT::
124
124
125 result : "Pulled from <reponame>"
125 id : <id_given_in_input>
126 result : "Pulled from `<reponame>`"
126 error : null
127 error : null
127
128
128
129
129 get_user
130 get_user
130 --------
131 --------
131
132
132 Get's an user by username or user_id, Returns empty result if user is not found.
133 Get's an user by username or user_id, Returns empty result if user is not found.
133 This command can be executed only using api_key belonging to user with admin
134 This command can be executed only using api_key belonging to user with admin
134 rights.
135 rights.
135
136
136
137
137 INPUT::
138 INPUT::
138
139
139 id : <id_for_response>
140 id : <id_for_response>
140 api_key : "<api_key>"
141 api_key : "<api_key>"
141 method : "get_user"
142 method : "get_user"
142 args : {
143 args : {
143 "userid" : "<username or user_id>"
144 "userid" : "<username or user_id>"
144 }
145 }
145
146
146 OUTPUT::
147 OUTPUT::
147
148
149 id : <id_given_in_input>
148 result: None if user does not exist or
150 result: None if user does not exist or
149 {
151 {
150 "id" : "<id>",
152 "user_id" : "<user_id>",
151 "username" : "<username>",
153 "username" : "<username>",
152 "firstname": "<firstname>",
154 "firstname": "<firstname>",
153 "lastname" : "<lastname>",
155 "lastname" : "<lastname>",
154 "email" : "<email>",
156 "email" : "<email>",
155 "emails": "<list_of_all_additional_emails>",
157 "emails": "<list_of_all_additional_emails>",
156 "active" : "<bool>",
158 "active" : "<bool>",
157 "admin" :Β  "<bool>",
159 "admin" :Β  "<bool>",
158 "ldap_dn" : "<ldap_dn>",
160 "ldap_dn" : "<ldap_dn>",
159 "last_login": "<last_login>",
161 "last_login": "<last_login>",
160 "permissions": {
162 "permissions": {
161 "global": ["hg.create.repository",
163 "global": ["hg.create.repository",
162 "repository.read",
164 "repository.read",
163 "hg.register.manual_activate"],
165 "hg.register.manual_activate"],
164 "repositories": {"repo1": "repository.none"},
166 "repositories": {"repo1": "repository.none"},
165 "repositories_groups": {"Group1": "group.read"}
167 "repositories_groups": {"Group1": "group.read"}
166 },
168 },
167 }
169 }
168
170
169 error: null
171 error: null
170
172
171
173
172 get_users
174 get_users
173 ---------
175 ---------
174
176
175 Lists all existing users. This command can be executed only using api_key
177 Lists all existing users. This command can be executed only using api_key
176 belonging to user with admin rights.
178 belonging to user with admin rights.
177
179
178
180
179 INPUT::
181 INPUT::
180
182
181 id : <id_for_response>
183 id : <id_for_response>
182 api_key : "<api_key>"
184 api_key : "<api_key>"
183 method : "get_users"
185 method : "get_users"
184 args : { }
186 args : { }
185
187
186 OUTPUT::
188 OUTPUT::
187
189
190 id : <id_given_in_input>
188 result: [
191 result: [
189 {
192 {
190 "id" : "<id>",
193 "user_id" : "<user_id>",
191 "username" : "<username>",
194 "username" : "<username>",
192 "firstname": "<firstname>",
195 "firstname": "<firstname>",
193 "lastname" : "<lastname>",
196 "lastname" : "<lastname>",
194 "email" : "<email>",
197 "email" : "<email>",
198 "emails": "<list_of_all_additional_emails>",
195 "active" : "<bool>",
199 "active" : "<bool>",
196 "admin" :Β  "<bool>",
200 "admin" :Β  "<bool>",
197 "ldap_dn" : "<ldap_dn>",
201 "ldap_dn" : "<ldap_dn>",
198 "last_login": "<last_login>",
202 "last_login": "<last_login>",
199 },
203 },
200 …
204 …
201 ]
205 ]
202 error: null
206 error: null
203
207
204
208
205 create_user
209 create_user
206 -----------
210 -----------
207
211
208 Creates new user. This command can
212 Creates new user. This command can
209 be executed only using api_key belonging to user with admin rights.
213 be executed only using api_key belonging to user with admin rights.
210
214
211
215
212 INPUT::
216 INPUT::
213
217
214 id : <id_for_response>
218 id : <id_for_response>
215 api_key : "<api_key>"
219 api_key : "<api_key>"
216 method : "create_user"
220 method : "create_user"
217 args : {
221 args : {
218 "username" : "<username>",
222 "username" : "<username>",
223 "email" : "<useremail>",
219 "password" : "<password>",
224 "password" : "<password>",
220 "email" : "<useremail>",
225 "firstname" : "<firstname> = Optional(None)",
221 "firstname" : "<firstname> = None",
226 "lastname" : "<lastname> = Optional(None)",
222 "lastname" : "<lastname> = None",
227 "active" : "<bool> = Optional(True)",
223 "active" : "<bool> = True",
228 "admin" : "<bool> = Optional(False)",
224 "admin" : "<bool> = False",
229 "ldap_dn" : "<ldap_dn> = Optional(None)"
225 "ldap_dn" : "<ldap_dn> = None"
226 }
230 }
227
231
228 OUTPUT::
232 OUTPUT::
229
233
234 id : <id_given_in_input>
230 result: {
235 result: {
231 "id" : "<new_user_id>",
236 "msg" : "created new user `<username>`",
232 "msg" : "created new user <username>",
233 "user": {
237 "user": {
234 "id" : "<id>",
238 "user_id" : "<user_id>",
235 "username" : "<username>",
239 "username" : "<username>",
236 "firstname": "<firstname>",
240 "firstname": "<firstname>",
237 "lastname" : "<lastname>",
241 "lastname" : "<lastname>",
238 "email" : "<email>",
242 "email" : "<email>",
243 "emails": "<list_of_all_additional_emails>",
239 "active" : "<bool>",
244 "active" : "<bool>",
240 "admin" :Β  "<bool>",
245 "admin" :Β  "<bool>",
241 "ldap_dn" : "<ldap_dn>",
246 "ldap_dn" : "<ldap_dn>",
242 "last_login": "<last_login>",
247 "last_login": "<last_login>",
243 },
248 },
244 }
249 }
245 error: null
250 error: null
246
251
247
252
248 update_user
253 update_user
249 -----------
254 -----------
250
255
251 updates given user if such user exists. This command can
256 updates given user if such user exists. This command can
252 be executed only using api_key belonging to user with admin rights.
257 be executed only using api_key belonging to user with admin rights.
253
258
254
259
255 INPUT::
260 INPUT::
256
261
257 id : <id_for_response>
262 id : <id_for_response>
258 api_key : "<api_key>"
263 api_key : "<api_key>"
259 method : "update_user"
264 method : "update_user"
260 args : {
265 args : {
261 "userid" : "<user_id or username>",
266 "userid" : "<user_id or username>",
262 "username" : "<username>",
267 "username" : "<username> = Optional",
263 "password" : "<password>",
268 "email" : "<useremail> = Optional",
264 "email" : "<useremail>",
269 "password" : "<password> = Optional",
265 "firstname" : "<firstname>",
270 "firstname" : "<firstname> = Optional",
266 "lastname" : "<lastname>",
271 "lastname" : "<lastname> = Optional",
267 "active" : "<bool>",
272 "active" : "<bool> = Optional",
268 "admin" : "<bool>",
273 "admin" : "<bool> = Optional",
269 "ldap_dn" : "<ldap_dn>"
274 "ldap_dn" : "<ldap_dn> = Optional"
270 }
275 }
271
276
272 OUTPUT::
277 OUTPUT::
273
278
279 id : <id_given_in_input>
274 result: {
280 result: {
275 "id" : "<edited_user_id>",
276 "msg" : "updated user ID:<userid> <username>",
281 "msg" : "updated user ID:<userid> <username>",
277 "user": {
282 "user": {
278 "id" : "<id>",
283 "user_id" : "<user_id>",
279 "username" : "<username>",
284 "username" : "<username>",
280 "firstname": "<firstname>",
285 "firstname": "<firstname>",
281 "lastname" : "<lastname>",
286 "lastname" : "<lastname>",
282 "email" : "<email>",
287 "email" : "<email>",
288 "emails": "<list_of_all_additional_emails>",
283 "active" : "<bool>",
289 "active" : "<bool>",
284 "admin" :Β  "<bool>",
290 "admin" :Β  "<bool>",
285 "ldap_dn" : "<ldap_dn>",
291 "ldap_dn" : "<ldap_dn>",
286 "last_login": "<last_login>",
292 "last_login": "<last_login>",
287 },
293 },
288 }
294 }
289 error: null
295 error: null
290
296
291
297
292 delete_user
298 delete_user
293 -----------
299 -----------
294
300
295
301
296 deletes givenuser if such user exists. This command can
302 deletes givenuser if such user exists. This command can
297 be executed only using api_key belonging to user with admin rights.
303 be executed only using api_key belonging to user with admin rights.
298
304
299
305
300 INPUT::
306 INPUT::
301
307
302 id : <id_for_response>
308 id : <id_for_response>
303 api_key : "<api_key>"
309 api_key : "<api_key>"
304 method : "delete_user"
310 method : "delete_user"
305 args : {
311 args : {
306 "userid" : "<user_id or username>",
312 "userid" : "<user_id or username>",
307 }
313 }
308
314
309 OUTPUT::
315 OUTPUT::
310
316
317 id : <id_given_in_input>
311 result: {
318 result: {
312 "id" : "<edited_user_id>",
319 "msg" : "deleted user ID:<userid> <username>",
313 "msg" : "deleted user ID:<userid> <username>"
320 "user": null
314 }
321 }
315 error: null
322 error: null
316
323
317
324
318 get_users_group
325 get_users_group
319 ---------------
326 ---------------
320
327
321 Gets an existing users group. This command can be executed only using api_key
328 Gets an existing users group. This command can be executed only using api_key
322 belonging to user with admin rights.
329 belonging to user with admin rights.
323
330
324
331
325 INPUT::
332 INPUT::
326
333
327 id : <id_for_response>
334 id : <id_for_response>
328 api_key : "<api_key>"
335 api_key : "<api_key>"
329 method : "get_users_group"
336 method : "get_users_group"
330 args : {
337 args : {
331 "group_name" : "<name>"
338 "usersgroupid" : "<users group id or name>"
332 }
339 }
333
340
334 OUTPUT::
341 OUTPUT::
335
342
343 id : <id_given_in_input>
336 result : None if group not exist
344 result : None if group not exist
337 {
345 {
338 "id" : "<id>",
346 "users_group_id" : "<id>",
339 "group_name" : "<groupname>",
347 "group_name" : "<groupname>",
340 "active": "<bool>",
348 "active": "<bool>",
341 "members" : [
349 "members" : [
342 { "id" : "<userid>",
350 {
351 "user_id" : "<user_id>",
343 "username" : "<username>",
352 "username" : "<username>",
344 "firstname": "<firstname>",
353 "firstname": "<firstname>",
345 "lastname" : "<lastname>",
354 "lastname" : "<lastname>",
346 "email" : "<email>",
355 "email" : "<email>",
356 "emails": "<list_of_all_additional_emails>",
347 "active" : "<bool>",
357 "active" : "<bool>",
348 "admin" :Β  "<bool>",
358 "admin" :Β  "<bool>",
349 "ldap" : "<ldap_dn>"
359 "ldap_dn" : "<ldap_dn>",
360 "last_login": "<last_login>",
350 },
361 },
351 …
362 …
352 ]
363 ]
353 }
364 }
354 error : null
365 error : null
355
366
356
367
357 get_users_groups
368 get_users_groups
358 ----------------
369 ----------------
359
370
360 Lists all existing users groups. This command can be executed only using
371 Lists all existing users groups. This command can be executed only using
361 api_key belonging to user with admin rights.
372 api_key belonging to user with admin rights.
362
373
363
374
364 INPUT::
375 INPUT::
365
376
366 id : <id_for_response>
377 id : <id_for_response>
367 api_key : "<api_key>"
378 api_key : "<api_key>"
368 method : "get_users_groups"
379 method : "get_users_groups"
369 args : { }
380 args : { }
370
381
371 OUTPUT::
382 OUTPUT::
372
383
384 id : <id_given_in_input>
373 result : [
385 result : [
374 {
386 {
375 "id" : "<id>",
387 "users_group_id" : "<id>",
376 "group_name" : "<groupname>",
388 "group_name" : "<groupname>",
377 "active": "<bool>",
389 "active": "<bool>",
378 "members" : [
390 "members" : [
379 {
391 {
380 "id" : "<userid>",
392 "user_id" : "<user_id>",
381 "username" : "<username>",
393 "username" : "<username>",
382 "firstname": "<firstname>",
394 "firstname": "<firstname>",
383 "lastname" : "<lastname>",
395 "lastname" : "<lastname>",
384 "email" : "<email>",
396 "email" : "<email>",
397 "emails": "<list_of_all_additional_emails>",
385 "active" : "<bool>",
398 "active" : "<bool>",
386 "admin" :Β  "<bool>",
399 "admin" :Β  "<bool>",
387 "ldap" : "<ldap_dn>"
400 "ldap_dn" : "<ldap_dn>",
401 "last_login": "<last_login>",
388 },
402 },
389 …
403 …
390 ]
404 ]
391 }
405 },
406 …
392 ]
407 ]
393 error : null
408 error : null
394
409
395
410
396 create_users_group
411 create_users_group
397 ------------------
412 ------------------
398
413
399 Creates new users group. This command can be executed only using api_key
414 Creates new users group. This command can be executed only using api_key
400 belonging to user with admin rights
415 belonging to user with admin rights
401
416
402
417
403 INPUT::
418 INPUT::
404
419
405 id : <id_for_response>
420 id : <id_for_response>
406 api_key : "<api_key>"
421 api_key : "<api_key>"
407 method : "create_users_group"
422 method : "create_users_group"
408 args: {
423 args: {
409 "group_name": "<groupname>",
424 "group_name": "<groupname>",
410 "active":"<bool> = True"
425 "active":"<bool> = Optional(True)"
411 }
426 }
412
427
413 OUTPUT::
428 OUTPUT::
414
429
430 id : <id_given_in_input>
415 result: {
431 result: {
416 "id": "<newusersgroupid>",
432 "msg": "created new users group `<groupname>`",
417 "msg": "created new users group <groupname>"
433 "users_group": {
434 "users_group_id" : "<id>",
435 "group_name" : "<groupname>",
436 "active": "<bool>",
437 "members" : [
438 {
439 "user_id" : "<user_id>",
440 "username" : "<username>",
441 "firstname": "<firstname>",
442 "lastname" : "<lastname>",
443 "email" : "<email>",
444 "emails": "<list_of_all_additional_emails>",
445 "active" : "<bool>",
446 "admin" :Β  "<bool>",
447 "ldap_dn" : "<ldap_dn>",
448 "last_login": "<last_login>",
449 },
450 …
451 ]
452 },
418 }
453 }
419 error: null
454 error: null
420
455
421
456
422 add_user_to_users_group
457 add_user_to_users_group
423 -----------------------
458 -----------------------
424
459
425 Adds a user to a users group. If user exists in that group success will be
460 Adds a user to a users group. If user exists in that group success will be
426 `false`. This command can be executed only using api_key
461 `false`. This command can be executed only using api_key
427 belonging to user with admin rights
462 belonging to user with admin rights
428
463
429
464
430 INPUT::
465 INPUT::
431
466
432 id : <id_for_response>
467 id : <id_for_response>
433 api_key : "<api_key>"
468 api_key : "<api_key>"
434 method : "add_user_users_group"
469 method : "add_user_users_group"
435 args: {
470 args: {
436 "group_name" : "<groupname>",
471 "usersgroupid" : "<users group id or name>",
437 "username" : "<username>"
472 "userid" : "<user_id or username>",
438 }
473 }
439
474
440 OUTPUT::
475 OUTPUT::
441
476
477 id : <id_given_in_input>
442 result: {
478 result: {
443 "id": "<newusersgroupmemberid>",
444 "success": True|False # depends on if member is in group
479 "success": True|False # depends on if member is in group
445 "msg": "added member <username> to users group <groupname> |
480 "msg": "added member `<username>` to users group `<groupname>` |
446 User is already in that group"
481 User is already in that group"
447 }
482 }
448 error: null
483 error: null
449
484
450
485
451 remove_user_from_users_group
486 remove_user_from_users_group
452 ----------------------------
487 ----------------------------
453
488
454 Removes a user from a users group. If user is not in given group success will
489 Removes a user from a users group. If user is not in given group success will
455 be `false`. This command can be executed only
490 be `false`. This command can be executed only
456 using api_key belonging to user with admin rights
491 using api_key belonging to user with admin rights
457
492
458
493
459 INPUT::
494 INPUT::
460
495
461 id : <id_for_response>
496 id : <id_for_response>
462 api_key : "<api_key>"
497 api_key : "<api_key>"
463 method : "remove_user_from_users_group"
498 method : "remove_user_from_users_group"
464 args: {
499 args: {
465 "group_name" : "<groupname>",
500 "usersgroupid" : "<users group id or name>",
466 "username" : "<username>"
501 "userid" : "<user_id or username>",
467 }
502 }
468
503
469 OUTPUT::
504 OUTPUT::
470
505
506 id : <id_given_in_input>
471 result: {
507 result: {
472 "success": True|False, # depends on if member is in group
508 "success": True|False, # depends on if member is in group
473 "msg": "removed member <username> from users group <groupname> |
509 "msg": "removed member <username> from users group <groupname> |
474 User wasn't in group"
510 User wasn't in group"
475 }
511 }
476 error: null
512 error: null
477
513
478
514
479 get_repo
515 get_repo
480 --------
516 --------
481
517
482 Gets an existing repository by it's name or repository_id. Members will return
518 Gets an existing repository by it's name or repository_id. Members will return
483 either users_group or user associated to that repository. This command can
519 either users_group or user associated to that repository. This command can
484 be executed only using api_key belonging to user with admin rights.
520 be executed only using api_key belonging to user with admin rights.
485
521
486
522
487 INPUT::
523 INPUT::
488
524
489 id : <id_for_response>
525 id : <id_for_response>
490 api_key : "<api_key>"
526 api_key : "<api_key>"
491 method : "get_repo"
527 method : "get_repo"
492 args: {
528 args: {
493 "repoid" : "<reponame or repo_id>"
529 "repoid" : "<reponame or repo_id>"
494 }
530 }
495
531
496 OUTPUT::
532 OUTPUT::
497
533
534 id : <id_given_in_input>
498 result: None if repository does not exist or
535 result: None if repository does not exist or
499 {
536 {
500 "id" : "<id>",
537 "repo_id" : "<repo_id>",
501 "repo_name" : "<reponame>"
538 "repo_name" : "<reponame>"
502 "type" : "<type>",
539 "repo_type" : "<repo_type>",
503 "description" : "<description>",
504 "clone_uri" : "<clone_uri>",
540 "clone_uri" : "<clone_uri>",
505 "private": : "<bool>",
541 "private": : "<bool>",
506 "created_on" : "<datetimecreated>",
542 "created_on" : "<datetimecreated>",
543 "description" : "<description>",
544 "landing_rev": "<landing_rev>",
545 "owner": "<repo_owner>",
546 "fork_of": "<name_of_fork_parent>",
507 "members" : [
547 "members" : [
508 {
548 {
509 "type": "user",
549 "type": "user",
510 "id" : "<userid>",
550 "user_id" : "<user_id>",
511 "username" : "<username>",
551 "username" : "<username>",
512 "firstname": "<firstname>",
552 "firstname": "<firstname>",
513 "lastname" : "<lastname>",
553 "lastname" : "<lastname>",
514 "email" : "<email>",
554 "email" : "<email>",
555 "emails": "<list_of_all_additional_emails>",
515 "active" : "<bool>",
556 "active" : "<bool>",
516 "admin" :Β  "<bool>",
557 "admin" :Β  "<bool>",
517 "ldap" : "<ldap_dn>",
558 "ldap_dn" : "<ldap_dn>",
559 "last_login": "<last_login>",
518 "permission" : "repository.(read|write|admin)"
560 "permission" : "repository.(read|write|admin)"
519 },
561 },
520 …
562 …
521 {
563 {
522 "type": "users_group",
564 "type": "users_group",
523 "id" : "<usersgroupid>",
565 "id" : "<usersgroupid>",
524 "name" : "<usersgroupname>",
566 "name" : "<usersgroupname>",
525 "active": "<bool>",
567 "active": "<bool>",
526 "permission" : "repository.(read|write|admin)"
568 "permission" : "repository.(read|write|admin)"
527 },
569 },
528 …
570 …
529 ]
571 ]
530 }
572 }
531 error: null
573 error: null
532
574
533
575
534 get_repos
576 get_repos
535 ---------
577 ---------
536
578
537 Lists all existing repositories. This command can be executed only using api_key
579 Lists all existing repositories. This command can be executed only using api_key
538 belonging to user with admin rights
580 belonging to user with admin rights
539
581
540
582
541 INPUT::
583 INPUT::
542
584
543 id : <id_for_response>
585 id : <id_for_response>
544 api_key : "<api_key>"
586 api_key : "<api_key>"
545 method : "get_repos"
587 method : "get_repos"
546 args: { }
588 args: { }
547
589
548 OUTPUT::
590 OUTPUT::
549
591
592 id : <id_given_in_input>
550 result: [
593 result: [
551 {
594 {
552 "id" : "<id>",
595 "repo_id" : "<repo_id>",
553 "repo_name" : "<reponame>"
596 "repo_name" : "<reponame>"
554 "type" : "<type>",
597 "repo_type" : "<repo_type>",
555 "description" : "<description>",
556 "clone_uri" : "<clone_uri>",
598 "clone_uri" : "<clone_uri>",
557 "private": : "<bool>",
599 "private": : "<bool>",
558 "created_on" : "<datetimecreated>",
600 "created_on" : "<datetimecreated>",
601 "description" : "<description>",
602 "landing_rev": "<landing_rev>",
603 "owner": "<repo_owner>",
604 "fork_of": "<name_of_fork_parent>",
559 },
605 },
560 …
606 …
561 ]
607 ]
562 error: null
608 error: null
563
609
564
610
565 get_repo_nodes
611 get_repo_nodes
566 --------------
612 --------------
567
613
568 returns a list of nodes and it's children in a flat list for a given path
614 returns a list of nodes and it's children in a flat list for a given path
569 at given revision. It's possible to specify ret_type to show only `files` or
615 at given revision. It's possible to specify ret_type to show only `files` or
570 `dirs`. This command can be executed only using api_key belonging to user
616 `dirs`. This command can be executed only using api_key belonging to user
571 with admin rights
617 with admin rights
572
618
573
619
574 INPUT::
620 INPUT::
575
621
576 id : <id_for_response>
622 id : <id_for_response>
577 api_key : "<api_key>"
623 api_key : "<api_key>"
578 method : "get_repo_nodes"
624 method : "get_repo_nodes"
579 args: {
625 args: {
580 "repo_name" : "<reponame>",
626 "repoid" : "<reponame or repo_id>"
581 "revision" : "<revision>",
627 "revision" : "<revision>",
582 "root_path" : "<root_path>",
628 "root_path" : "<root_path>",
583 "ret_type" : "<ret_type>" = 'all'
629 "ret_type" : "<ret_type> = Optional('all')"
584 }
630 }
585
631
586 OUTPUT::
632 OUTPUT::
587
633
634 id : <id_given_in_input>
588 result: [
635 result: [
589 {
636 {
590 "name" : "<name>"
637 "name" : "<name>"
591 "type" : "<type>",
638 "type" : "<type>",
592 },
639 },
593 …
640 …
594 ]
641 ]
595 error: null
642 error: null
596
643
597
644
598 create_repo
645 create_repo
599 -----------
646 -----------
600
647
601 Creates a repository. This command can be executed only using api_key
648 Creates a repository. This command can be executed only using api_key
602 belonging to user with admin rights.
649 belonging to user with admin rights.
603 If repository name contains "/", all needed repository groups will be created.
650 If repository name contains "/", all needed repository groups will be created.
604 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
651 For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent),
605 and create "baz" repository with "bar" as group.
652 and create "baz" repository with "bar" as group.
606
653
607
654
608 INPUT::
655 INPUT::
609
656
610 id : <id_for_response>
657 id : <id_for_response>
611 api_key : "<api_key>"
658 api_key : "<api_key>"
612 method : "create_repo"
659 method : "create_repo"
613 args: {
660 args: {
614 "repo_name" : "<reponame>",
661 "repo_name" : "<reponame>",
615 "owner_name" : "<ownername>",
662 "owner" : "<onwer_name_or_id>",
616 "description" : "<description> = ''",
663 "repo_type" : "<repo_type>",
617 "repo_type" : "<type> = 'hg'",
664 "description" : "<description> = Optional('')",
618 "private" : "<bool> = False",
665 "private" : "<bool> = Optional(False)",
619 "clone_uri" : "<clone_uri> = None",
666 "clone_uri" : "<clone_uri> = Optional(None)",
667 "landing_rev" : "<landing_rev> = Optional('tip')",
620 }
668 }
621
669
622 OUTPUT::
670 OUTPUT::
623
671
672 id : <id_given_in_input>
624 result: {
673 result: {
625 "id": "<newrepoid>",
674 "msg": "Created new repository `<reponame>`",
626 "msg": "Created new repository <reponame>",
627 "repo": {
675 "repo": {
628 "id" : "<id>",
676 "repo_id" : "<repo_id>",
629 "repo_name" : "<reponame>"
677 "repo_name" : "<reponame>"
630 "type" : "<type>",
678 "repo_type" : "<repo_type>",
631 "description" : "<description>",
632 "clone_uri" : "<clone_uri>",
679 "clone_uri" : "<clone_uri>",
633 "private": : "<bool>",
680 "private": : "<bool>",
634 "created_on" : "<datetimecreated>",
681 "created_on" : "<datetimecreated>",
682 "description" : "<description>",
683 "landing_rev": "<landing_rev>",
684 "owner": "<repo_owner>",
685 "fork_of": "<name_of_fork_parent>",
635 },
686 },
636 }
687 }
637 error: null
688 error: null
638
689
639
690
640 delete_repo
691 delete_repo
641 -----------
692 -----------
642
693
643 Deletes a repository. This command can be executed only using api_key
694 Deletes a repository. This command can be executed only using api_key
644 belonging to user with admin rights.
695 belonging to user with admin rights.
645
696
646
697
647 INPUT::
698 INPUT::
648
699
649 id : <id_for_response>
700 id : <id_for_response>
650 api_key : "<api_key>"
701 api_key : "<api_key>"
651 method : "delete_repo"
702 method : "delete_repo"
652 args: {
703 args: {
653 "repo_name" : "<reponame>",
704 "repoid" : "<reponame or repo_id>"
654 }
705 }
655
706
656 OUTPUT::
707 OUTPUT::
657
708
709 id : <id_given_in_input>
658 result: {
710 result: {
659 "msg": "Deleted repository <reponame>",
711 "msg": "Deleted repository `<reponame>`",
712 "success": true
660 }
713 }
661 error: null
714 error: null
662
715
663
716
664 grant_user_permission
717 grant_user_permission
665 ---------------------
718 ---------------------
666
719
667 Grant permission for user on given repository, or update existing one
720 Grant permission for user on given repository, or update existing one
668 if found. This command can be executed only using api_key belonging to user
721 if found. This command can be executed only using api_key belonging to user
669 with admin rights.
722 with admin rights.
670
723
671
724
672 INPUT::
725 INPUT::
673
726
674 id : <id_for_response>
727 id : <id_for_response>
675 api_key : "<api_key>"
728 api_key : "<api_key>"
676 method : "grant_user_permission"
729 method : "grant_user_permission"
677 args: {
730 args: {
678 "repo_name" : "<reponame>",
731 "repoid" : "<reponame or repo_id>"
679 "username" : "<username>",
732 "userid" : "<username or user_id>"
680 "perm" : "(repository.(none|read|write|admin))",
733 "perm" : "(repository.(none|read|write|admin))",
681 }
734 }
682
735
683 OUTPUT::
736 OUTPUT::
684
737
738 id : <id_given_in_input>
685 result: {
739 result: {
686 "msg" : "Granted perm: <perm> for user: <username> in repo: <reponame>"
740 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
741 "success": true
687 }
742 }
688 error: null
743 error: null
689
744
690
745
691 revoke_user_permission
746 revoke_user_permission
692 ----------------------
747 ----------------------
693
748
694 Revoke permission for user on given repository. This command can be executed
749 Revoke permission for user on given repository. This command can be executed
695 only using api_key belonging to user with admin rights.
750 only using api_key belonging to user with admin rights.
696
751
697
752
698 INPUT::
753 INPUT::
699
754
700 id : <id_for_response>
755 id : <id_for_response>
701 api_key : "<api_key>"
756 api_key : "<api_key>"
702 method : "revoke_user_permission"
757 method : "revoke_user_permission"
703 args: {
758 args: {
704 "repo_name" : "<reponame>",
759 "repoid" : "<reponame or repo_id>"
705 "username" : "<username>",
760 "userid" : "<username or user_id>"
706 }
761 }
707
762
708 OUTPUT::
763 OUTPUT::
709
764
765 id : <id_given_in_input>
710 result: {
766 result: {
711 "msg" : "Revoked perm for user: <suername> in repo: <reponame>"
767 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
768 "success": true
712 }
769 }
713 error: null
770 error: null
714
771
715
772
716 grant_users_group_permission
773 grant_users_group_permission
717 ----------------------------
774 ----------------------------
718
775
719 Grant permission for users group on given repository, or update
776 Grant permission for users group on given repository, or update
720 existing one if found. This command can be executed only using
777 existing one if found. This command can be executed only using
721 api_key belonging to user with admin rights.
778 api_key belonging to user with admin rights.
722
779
723
780
724 INPUT::
781 INPUT::
725
782
726 id : <id_for_response>
783 id : <id_for_response>
727 api_key : "<api_key>"
784 api_key : "<api_key>"
728 method : "grant_users_group_permission"
785 method : "grant_users_group_permission"
729 args: {
786 args: {
730 "repo_name" : "<reponame>",
787 "repoid" : "<reponame or repo_id>"
731 "group_name" : "<usersgroupname>",
788 "usersgroupid" : "<users group id or name>"
732 "perm" : "(repository.(none|read|write|admin))",
789 "perm" : "(repository.(none|read|write|admin))",
733 }
790 }
734
791
735 OUTPUT::
792 OUTPUT::
736
793
794 id : <id_given_in_input>
737 result: {
795 result: {
738 "msg" : "Granted perm: <perm> for group: <usersgroupname> in repo: <reponame>"
796 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
797 "success": true
739 }
798 }
740 error: null
799 error: null
741
800
742
801
743 revoke_users_group_permission
802 revoke_users_group_permission
744 -----------------------------
803 -----------------------------
745
804
746 Revoke permission for users group on given repository.This command can be
805 Revoke permission for users group on given repository.This command can be
747 executed only using api_key belonging to user with admin rights.
806 executed only using api_key belonging to user with admin rights.
748
807
749 INPUT::
808 INPUT::
750
809
751 id : <id_for_response>
810 id : <id_for_response>
752 api_key : "<api_key>"
811 api_key : "<api_key>"
753 method : "revoke_users_group_permission"
812 method : "revoke_users_group_permission"
754 args: {
813 args: {
755 "repo_name" : "<reponame>",
814 "repoid" : "<reponame or repo_id>"
756 "users_group" : "<usersgroupname>",
815 "usersgroupid" : "<users group id or name>"
757 }
816 }
758
817
759 OUTPUT::
818 OUTPUT::
760
819
820 id : <id_given_in_input>
761 result: {
821 result: {
762 "msg" : "Revoked perm for group: <usersgroupname> in repo: <reponame>"
822 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
823 "success": true
763 }
824 }
764 error: null No newline at end of file
825 error: null
@@ -1,1635 +1,1635 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38 from webob.exc import HTTPNotFound
38 from webob.exc import HTTPNotFound
39
39
40 from pylons.i18n.translation import lazy_ugettext as _
40 from pylons.i18n.translation import lazy_ugettext as _
41
41
42 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46
46
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 safe_unicode
48 safe_unicode
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model.meta import Base, Session
52 from rhodecode.model.meta import Base, Session
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class ModelSerializer(json.JSONEncoder):
64 class ModelSerializer(json.JSONEncoder):
65 """
65 """
66 Simple Serializer for JSON,
66 Simple Serializer for JSON,
67
67
68 usage::
68 usage::
69
69
70 to make object customized for serialization implement a __json__
70 to make object customized for serialization implement a __json__
71 method that will return a dict for serialization into json
71 method that will return a dict for serialization into json
72
72
73 example::
73 example::
74
74
75 class Task(object):
75 class Task(object):
76
76
77 def __init__(self, name, value):
77 def __init__(self, name, value):
78 self.name = name
78 self.name = name
79 self.value = value
79 self.value = value
80
80
81 def __json__(self):
81 def __json__(self):
82 return dict(name=self.name,
82 return dict(name=self.name,
83 value=self.value)
83 value=self.value)
84
84
85 """
85 """
86
86
87 def default(self, obj):
87 def default(self, obj):
88
88
89 if hasattr(obj, '__json__'):
89 if hasattr(obj, '__json__'):
90 return obj.__json__()
90 return obj.__json__()
91 else:
91 else:
92 return json.JSONEncoder.default(self, obj)
92 return json.JSONEncoder.default(self, obj)
93
93
94
94
95 class BaseModel(object):
95 class BaseModel(object):
96 """
96 """
97 Base Model for all classess
97 Base Model for all classess
98 """
98 """
99
99
100 @classmethod
100 @classmethod
101 def _get_keys(cls):
101 def _get_keys(cls):
102 """return column names for this model """
102 """return column names for this model """
103 return class_mapper(cls).c.keys()
103 return class_mapper(cls).c.keys()
104
104
105 def get_dict(self):
105 def get_dict(self):
106 """
106 """
107 return dict with keys and values corresponding
107 return dict with keys and values corresponding
108 to this model data """
108 to this model data """
109
109
110 d = {}
110 d = {}
111 for k in self._get_keys():
111 for k in self._get_keys():
112 d[k] = getattr(self, k)
112 d[k] = getattr(self, k)
113
113
114 # also use __json__() if present to get additional fields
114 # also use __json__() if present to get additional fields
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
116 d[k] = val
116 d[k] = val
117 return d
117 return d
118
118
119 def get_appstruct(self):
119 def get_appstruct(self):
120 """return list with keys and values tupples corresponding
120 """return list with keys and values tupples corresponding
121 to this model data """
121 to this model data """
122
122
123 l = []
123 l = []
124 for k in self._get_keys():
124 for k in self._get_keys():
125 l.append((k, getattr(self, k),))
125 l.append((k, getattr(self, k),))
126 return l
126 return l
127
127
128 def populate_obj(self, populate_dict):
128 def populate_obj(self, populate_dict):
129 """populate model with data from given populate_dict"""
129 """populate model with data from given populate_dict"""
130
130
131 for k in self._get_keys():
131 for k in self._get_keys():
132 if k in populate_dict:
132 if k in populate_dict:
133 setattr(self, k, populate_dict[k])
133 setattr(self, k, populate_dict[k])
134
134
135 @classmethod
135 @classmethod
136 def query(cls):
136 def query(cls):
137 return Session().query(cls)
137 return Session().query(cls)
138
138
139 @classmethod
139 @classmethod
140 def get(cls, id_):
140 def get(cls, id_):
141 if id_:
141 if id_:
142 return cls.query().get(id_)
142 return cls.query().get(id_)
143
143
144 @classmethod
144 @classmethod
145 def get_or_404(cls, id_):
145 def get_or_404(cls, id_):
146 if id_:
146 if id_:
147 res = cls.query().get(id_)
147 res = cls.query().get(id_)
148 if not res:
148 if not res:
149 raise HTTPNotFound
149 raise HTTPNotFound
150 return res
150 return res
151
151
152 @classmethod
152 @classmethod
153 def getAll(cls):
153 def getAll(cls):
154 return cls.query().all()
154 return cls.query().all()
155
155
156 @classmethod
156 @classmethod
157 def delete(cls, id_):
157 def delete(cls, id_):
158 obj = cls.query().get(id_)
158 obj = cls.query().get(id_)
159 Session().delete(obj)
159 Session().delete(obj)
160
160
161 def __repr__(self):
161 def __repr__(self):
162 if hasattr(self, '__unicode__'):
162 if hasattr(self, '__unicode__'):
163 # python repr needs to return str
163 # python repr needs to return str
164 return safe_str(self.__unicode__())
164 return safe_str(self.__unicode__())
165 return '<DB:%s>' % (self.__class__.__name__)
165 return '<DB:%s>' % (self.__class__.__name__)
166
166
167
167
168 class RhodeCodeSetting(Base, BaseModel):
168 class RhodeCodeSetting(Base, BaseModel):
169 __tablename__ = 'rhodecode_settings'
169 __tablename__ = 'rhodecode_settings'
170 __table_args__ = (
170 __table_args__ = (
171 UniqueConstraint('app_settings_name'),
171 UniqueConstraint('app_settings_name'),
172 {'extend_existing': True, 'mysql_engine': 'InnoDB',
172 {'extend_existing': True, 'mysql_engine': 'InnoDB',
173 'mysql_charset': 'utf8'}
173 'mysql_charset': 'utf8'}
174 )
174 )
175 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
175 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
176 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
176 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
178
178
179 def __init__(self, k='', v=''):
179 def __init__(self, k='', v=''):
180 self.app_settings_name = k
180 self.app_settings_name = k
181 self.app_settings_value = v
181 self.app_settings_value = v
182
182
183 @validates('_app_settings_value')
183 @validates('_app_settings_value')
184 def validate_settings_value(self, key, val):
184 def validate_settings_value(self, key, val):
185 assert type(val) == unicode
185 assert type(val) == unicode
186 return val
186 return val
187
187
188 @hybrid_property
188 @hybrid_property
189 def app_settings_value(self):
189 def app_settings_value(self):
190 v = self._app_settings_value
190 v = self._app_settings_value
191 if self.app_settings_name == 'ldap_active':
191 if self.app_settings_name == 'ldap_active':
192 v = str2bool(v)
192 v = str2bool(v)
193 return v
193 return v
194
194
195 @app_settings_value.setter
195 @app_settings_value.setter
196 def app_settings_value(self, val):
196 def app_settings_value(self, val):
197 """
197 """
198 Setter that will always make sure we use unicode in app_settings_value
198 Setter that will always make sure we use unicode in app_settings_value
199
199
200 :param val:
200 :param val:
201 """
201 """
202 self._app_settings_value = safe_unicode(val)
202 self._app_settings_value = safe_unicode(val)
203
203
204 def __unicode__(self):
204 def __unicode__(self):
205 return u"<%s('%s:%s')>" % (
205 return u"<%s('%s:%s')>" % (
206 self.__class__.__name__,
206 self.__class__.__name__,
207 self.app_settings_name, self.app_settings_value
207 self.app_settings_name, self.app_settings_value
208 )
208 )
209
209
210 @classmethod
210 @classmethod
211 def get_by_name(cls, ldap_key):
211 def get_by_name(cls, ldap_key):
212 return cls.query()\
212 return cls.query()\
213 .filter(cls.app_settings_name == ldap_key).scalar()
213 .filter(cls.app_settings_name == ldap_key).scalar()
214
214
215 @classmethod
215 @classmethod
216 def get_app_settings(cls, cache=False):
216 def get_app_settings(cls, cache=False):
217
217
218 ret = cls.query()
218 ret = cls.query()
219
219
220 if cache:
220 if cache:
221 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
221 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
222
222
223 if not ret:
223 if not ret:
224 raise Exception('Could not get application settings !')
224 raise Exception('Could not get application settings !')
225 settings = {}
225 settings = {}
226 for each in ret:
226 for each in ret:
227 settings['rhodecode_' + each.app_settings_name] = \
227 settings['rhodecode_' + each.app_settings_name] = \
228 each.app_settings_value
228 each.app_settings_value
229
229
230 return settings
230 return settings
231
231
232 @classmethod
232 @classmethod
233 def get_ldap_settings(cls, cache=False):
233 def get_ldap_settings(cls, cache=False):
234 ret = cls.query()\
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('ldap_')).all()
235 .filter(cls.app_settings_name.startswith('ldap_')).all()
236 fd = {}
236 fd = {}
237 for row in ret:
237 for row in ret:
238 fd.update({row.app_settings_name: row.app_settings_value})
238 fd.update({row.app_settings_name: row.app_settings_value})
239
239
240 return fd
240 return fd
241
241
242
242
243 class RhodeCodeUi(Base, BaseModel):
243 class RhodeCodeUi(Base, BaseModel):
244 __tablename__ = 'rhodecode_ui'
244 __tablename__ = 'rhodecode_ui'
245 __table_args__ = (
245 __table_args__ = (
246 UniqueConstraint('ui_key'),
246 UniqueConstraint('ui_key'),
247 {'extend_existing': True, 'mysql_engine': 'InnoDB',
247 {'extend_existing': True, 'mysql_engine': 'InnoDB',
248 'mysql_charset': 'utf8'}
248 'mysql_charset': 'utf8'}
249 )
249 )
250
250
251 HOOK_UPDATE = 'changegroup.update'
251 HOOK_UPDATE = 'changegroup.update'
252 HOOK_REPO_SIZE = 'changegroup.repo_size'
252 HOOK_REPO_SIZE = 'changegroup.repo_size'
253 HOOK_PUSH = 'changegroup.push_logger'
253 HOOK_PUSH = 'changegroup.push_logger'
254 HOOK_PULL = 'preoutgoing.pull_logger'
254 HOOK_PULL = 'preoutgoing.pull_logger'
255
255
256 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
256 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
257 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
257 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
258 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
258 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
259 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
259 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
260 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
260 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
261
261
262 @classmethod
262 @classmethod
263 def get_by_key(cls, key):
263 def get_by_key(cls, key):
264 return cls.query().filter(cls.ui_key == key)
264 return cls.query().filter(cls.ui_key == key)
265
265
266 @classmethod
266 @classmethod
267 def get_builtin_hooks(cls):
267 def get_builtin_hooks(cls):
268 q = cls.query()
268 q = cls.query()
269 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
269 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
270 cls.HOOK_REPO_SIZE,
270 cls.HOOK_REPO_SIZE,
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
272 return q.all()
272 return q.all()
273
273
274 @classmethod
274 @classmethod
275 def get_custom_hooks(cls):
275 def get_custom_hooks(cls):
276 q = cls.query()
276 q = cls.query()
277 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
277 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
278 cls.HOOK_REPO_SIZE,
278 cls.HOOK_REPO_SIZE,
279 cls.HOOK_PUSH, cls.HOOK_PULL]))
279 cls.HOOK_PUSH, cls.HOOK_PULL]))
280 q = q.filter(cls.ui_section == 'hooks')
280 q = q.filter(cls.ui_section == 'hooks')
281 return q.all()
281 return q.all()
282
282
283 @classmethod
283 @classmethod
284 def get_repos_location(cls):
284 def get_repos_location(cls):
285 return cls.get_by_key('/').one().ui_value
285 return cls.get_by_key('/').one().ui_value
286
286
287 @classmethod
287 @classmethod
288 def create_or_update_hook(cls, key, val):
288 def create_or_update_hook(cls, key, val):
289 new_ui = cls.get_by_key(key).scalar() or cls()
289 new_ui = cls.get_by_key(key).scalar() or cls()
290 new_ui.ui_section = 'hooks'
290 new_ui.ui_section = 'hooks'
291 new_ui.ui_active = True
291 new_ui.ui_active = True
292 new_ui.ui_key = key
292 new_ui.ui_key = key
293 new_ui.ui_value = val
293 new_ui.ui_value = val
294
294
295 Session().add(new_ui)
295 Session().add(new_ui)
296
296
297
297
298 class User(Base, BaseModel):
298 class User(Base, BaseModel):
299 __tablename__ = 'users'
299 __tablename__ = 'users'
300 __table_args__ = (
300 __table_args__ = (
301 UniqueConstraint('username'), UniqueConstraint('email'),
301 UniqueConstraint('username'), UniqueConstraint('email'),
302 {'extend_existing': True, 'mysql_engine': 'InnoDB',
302 {'extend_existing': True, 'mysql_engine': 'InnoDB',
303 'mysql_charset': 'utf8'}
303 'mysql_charset': 'utf8'}
304 )
304 )
305 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
306 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
308 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
308 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
309 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
309 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
310 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
313 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
313 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
314 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
316
316
317 user_log = relationship('UserLog', cascade='all')
317 user_log = relationship('UserLog', cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
319
319
320 repositories = relationship('Repository')
320 repositories = relationship('Repository')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
324
324
325 group_member = relationship('UsersGroupMember', cascade='all')
325 group_member = relationship('UsersGroupMember', cascade='all')
326
326
327 notifications = relationship('UserNotification', cascade='all')
327 notifications = relationship('UserNotification', cascade='all')
328 # notifications assigned to this user
328 # notifications assigned to this user
329 user_created_notifications = relationship('Notification', cascade='all')
329 user_created_notifications = relationship('Notification', cascade='all')
330 # comments created by this user
330 # comments created by this user
331 user_comments = relationship('ChangesetComment', cascade='all')
331 user_comments = relationship('ChangesetComment', cascade='all')
332
332
333 @hybrid_property
333 @hybrid_property
334 def email(self):
334 def email(self):
335 return self._email
335 return self._email
336
336
337 @email.setter
337 @email.setter
338 def email(self, val):
338 def email(self, val):
339 self._email = val.lower() if val else None
339 self._email = val.lower() if val else None
340
340
341 @property
341 @property
342 def emails(self):
342 def emails(self):
343 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
343 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
344 return [self.email] + [x.email for x in other]
344 return [self.email] + [x.email for x in other]
345
345
346 @property
346 @property
347 def full_name(self):
347 def full_name(self):
348 return '%s %s' % (self.name, self.lastname)
348 return '%s %s' % (self.name, self.lastname)
349
349
350 @property
350 @property
351 def full_name_or_username(self):
351 def full_name_or_username(self):
352 return ('%s %s' % (self.name, self.lastname)
352 return ('%s %s' % (self.name, self.lastname)
353 if (self.name and self.lastname) else self.username)
353 if (self.name and self.lastname) else self.username)
354
354
355 @property
355 @property
356 def full_contact(self):
356 def full_contact(self):
357 return '%s %s <%s>' % (self.name, self.lastname, self.email)
357 return '%s %s <%s>' % (self.name, self.lastname, self.email)
358
358
359 @property
359 @property
360 def short_contact(self):
360 def short_contact(self):
361 return '%s %s' % (self.name, self.lastname)
361 return '%s %s' % (self.name, self.lastname)
362
362
363 @property
363 @property
364 def is_admin(self):
364 def is_admin(self):
365 return self.admin
365 return self.admin
366
366
367 def __unicode__(self):
367 def __unicode__(self):
368 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
368 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
369 self.user_id, self.username)
369 self.user_id, self.username)
370
370
371 @classmethod
371 @classmethod
372 def get_by_username(cls, username, case_insensitive=False, cache=False):
372 def get_by_username(cls, username, case_insensitive=False, cache=False):
373 if case_insensitive:
373 if case_insensitive:
374 q = cls.query().filter(cls.username.ilike(username))
374 q = cls.query().filter(cls.username.ilike(username))
375 else:
375 else:
376 q = cls.query().filter(cls.username == username)
376 q = cls.query().filter(cls.username == username)
377
377
378 if cache:
378 if cache:
379 q = q.options(FromCache(
379 q = q.options(FromCache(
380 "sql_cache_short",
380 "sql_cache_short",
381 "get_user_%s" % _hash_key(username)
381 "get_user_%s" % _hash_key(username)
382 )
382 )
383 )
383 )
384 return q.scalar()
384 return q.scalar()
385
385
386 @classmethod
386 @classmethod
387 def get_by_api_key(cls, api_key, cache=False):
387 def get_by_api_key(cls, api_key, cache=False):
388 q = cls.query().filter(cls.api_key == api_key)
388 q = cls.query().filter(cls.api_key == api_key)
389
389
390 if cache:
390 if cache:
391 q = q.options(FromCache("sql_cache_short",
391 q = q.options(FromCache("sql_cache_short",
392 "get_api_key_%s" % api_key))
392 "get_api_key_%s" % api_key))
393 return q.scalar()
393 return q.scalar()
394
394
395 @classmethod
395 @classmethod
396 def get_by_email(cls, email, case_insensitive=False, cache=False):
396 def get_by_email(cls, email, case_insensitive=False, cache=False):
397 if case_insensitive:
397 if case_insensitive:
398 q = cls.query().filter(cls.email.ilike(email))
398 q = cls.query().filter(cls.email.ilike(email))
399 else:
399 else:
400 q = cls.query().filter(cls.email == email)
400 q = cls.query().filter(cls.email == email)
401
401
402 if cache:
402 if cache:
403 q = q.options(FromCache("sql_cache_short",
403 q = q.options(FromCache("sql_cache_short",
404 "get_email_key_%s" % email))
404 "get_email_key_%s" % email))
405
405
406 ret = q.scalar()
406 ret = q.scalar()
407 if ret is None:
407 if ret is None:
408 q = UserEmailMap.query()
408 q = UserEmailMap.query()
409 # try fetching in alternate email map
409 # try fetching in alternate email map
410 if case_insensitive:
410 if case_insensitive:
411 q = q.filter(UserEmailMap.email.ilike(email))
411 q = q.filter(UserEmailMap.email.ilike(email))
412 else:
412 else:
413 q = q.filter(UserEmailMap.email == email)
413 q = q.filter(UserEmailMap.email == email)
414 q = q.options(joinedload(UserEmailMap.user))
414 q = q.options(joinedload(UserEmailMap.user))
415 if cache:
415 if cache:
416 q = q.options(FromCache("sql_cache_short",
416 q = q.options(FromCache("sql_cache_short",
417 "get_email_map_key_%s" % email))
417 "get_email_map_key_%s" % email))
418 ret = getattr(q.scalar(), 'user', None)
418 ret = getattr(q.scalar(), 'user', None)
419
419
420 return ret
420 return ret
421
421
422 def update_lastlogin(self):
422 def update_lastlogin(self):
423 """Update user lastlogin"""
423 """Update user lastlogin"""
424 self.last_login = datetime.datetime.now()
424 self.last_login = datetime.datetime.now()
425 Session().add(self)
425 Session().add(self)
426 log.debug('updated user %s lastlogin' % self.username)
426 log.debug('updated user %s lastlogin' % self.username)
427
427
428 def get_api_data(self):
428 def get_api_data(self):
429 """
429 """
430 Common function for generating user related data for API
430 Common function for generating user related data for API
431 """
431 """
432 user = self
432 user = self
433 data = dict(
433 data = dict(
434 user_id=user.user_id,
434 user_id=user.user_id,
435 username=user.username,
435 username=user.username,
436 firstname=user.name,
436 firstname=user.name,
437 lastname=user.lastname,
437 lastname=user.lastname,
438 email=user.email,
438 email=user.email,
439 emails=user.emails,
439 emails=user.emails,
440 api_key=user.api_key,
440 api_key=user.api_key,
441 active=user.active,
441 active=user.active,
442 admin=user.admin,
442 admin=user.admin,
443 ldap_dn=user.ldap_dn,
443 ldap_dn=user.ldap_dn,
444 last_login=user.last_login,
444 last_login=user.last_login,
445 )
445 )
446 return data
446 return data
447
447
448 def __json__(self):
448 def __json__(self):
449 return dict(
449 return dict(
450 user_id=self.user_id,
450 user_id=self.user_id,
451 first_name=self.name,
451 first_name=self.name,
452 last_name=self.lastname,
452 last_name=self.lastname,
453 email=self.email,
453 email=self.email,
454 full_name=self.full_name,
454 full_name=self.full_name,
455 full_name_or_username=self.full_name_or_username,
455 full_name_or_username=self.full_name_or_username,
456 short_contact=self.short_contact,
456 short_contact=self.short_contact,
457 full_contact=self.full_contact
457 full_contact=self.full_contact
458 )
458 )
459
459
460
460
461 class UserEmailMap(Base, BaseModel):
461 class UserEmailMap(Base, BaseModel):
462 __tablename__ = 'user_email_map'
462 __tablename__ = 'user_email_map'
463 __table_args__ = (
463 __table_args__ = (
464 Index('uem_email_idx', 'email'),
464 Index('uem_email_idx', 'email'),
465 UniqueConstraint('email'),
465 UniqueConstraint('email'),
466 {'extend_existing': True, 'mysql_engine': 'InnoDB',
466 {'extend_existing': True, 'mysql_engine': 'InnoDB',
467 'mysql_charset': 'utf8'}
467 'mysql_charset': 'utf8'}
468 )
468 )
469 __mapper_args__ = {}
469 __mapper_args__ = {}
470
470
471 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
471 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
473 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
473 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
474
474
475 user = relationship('User', lazy='joined')
475 user = relationship('User', lazy='joined')
476
476
477 @validates('_email')
477 @validates('_email')
478 def validate_email(self, key, email):
478 def validate_email(self, key, email):
479 # check if this email is not main one
479 # check if this email is not main one
480 main_email = Session().query(User).filter(User.email == email).scalar()
480 main_email = Session().query(User).filter(User.email == email).scalar()
481 if main_email is not None:
481 if main_email is not None:
482 raise AttributeError('email %s is present is user table' % email)
482 raise AttributeError('email %s is present is user table' % email)
483 return email
483 return email
484
484
485 @hybrid_property
485 @hybrid_property
486 def email(self):
486 def email(self):
487 return self._email
487 return self._email
488
488
489 @email.setter
489 @email.setter
490 def email(self, val):
490 def email(self, val):
491 self._email = val.lower() if val else None
491 self._email = val.lower() if val else None
492
492
493
493
494 class UserLog(Base, BaseModel):
494 class UserLog(Base, BaseModel):
495 __tablename__ = 'user_logs'
495 __tablename__ = 'user_logs'
496 __table_args__ = (
496 __table_args__ = (
497 {'extend_existing': True, 'mysql_engine': 'InnoDB',
497 {'extend_existing': True, 'mysql_engine': 'InnoDB',
498 'mysql_charset': 'utf8'},
498 'mysql_charset': 'utf8'},
499 )
499 )
500 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
500 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
501 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
501 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
502 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
502 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
503 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
503 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
505 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
505 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
506 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
506 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
507
507
508 @property
508 @property
509 def action_as_day(self):
509 def action_as_day(self):
510 return datetime.date(*self.action_date.timetuple()[:3])
510 return datetime.date(*self.action_date.timetuple()[:3])
511
511
512 user = relationship('User')
512 user = relationship('User')
513 repository = relationship('Repository', cascade='')
513 repository = relationship('Repository', cascade='')
514
514
515
515
516 class UsersGroup(Base, BaseModel):
516 class UsersGroup(Base, BaseModel):
517 __tablename__ = 'users_groups'
517 __tablename__ = 'users_groups'
518 __table_args__ = (
518 __table_args__ = (
519 {'extend_existing': True, 'mysql_engine': 'InnoDB',
519 {'extend_existing': True, 'mysql_engine': 'InnoDB',
520 'mysql_charset': 'utf8'},
520 'mysql_charset': 'utf8'},
521 )
521 )
522
522
523 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
523 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
524 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
524 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
525 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
525 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
526
526
527 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
527 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
528 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
528 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
529 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
529 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
530
530
531 def __unicode__(self):
531 def __unicode__(self):
532 return u'<userGroup(%s)>' % (self.users_group_name)
532 return u'<userGroup(%s)>' % (self.users_group_name)
533
533
534 @classmethod
534 @classmethod
535 def get_by_group_name(cls, group_name, cache=False,
535 def get_by_group_name(cls, group_name, cache=False,
536 case_insensitive=False):
536 case_insensitive=False):
537 if case_insensitive:
537 if case_insensitive:
538 q = cls.query().filter(cls.users_group_name.ilike(group_name))
538 q = cls.query().filter(cls.users_group_name.ilike(group_name))
539 else:
539 else:
540 q = cls.query().filter(cls.users_group_name == group_name)
540 q = cls.query().filter(cls.users_group_name == group_name)
541 if cache:
541 if cache:
542 q = q.options(FromCache(
542 q = q.options(FromCache(
543 "sql_cache_short",
543 "sql_cache_short",
544 "get_user_%s" % _hash_key(group_name)
544 "get_user_%s" % _hash_key(group_name)
545 )
545 )
546 )
546 )
547 return q.scalar()
547 return q.scalar()
548
548
549 @classmethod
549 @classmethod
550 def get(cls, users_group_id, cache=False):
550 def get(cls, users_group_id, cache=False):
551 users_group = cls.query()
551 users_group = cls.query()
552 if cache:
552 if cache:
553 users_group = users_group.options(FromCache("sql_cache_short",
553 users_group = users_group.options(FromCache("sql_cache_short",
554 "get_users_group_%s" % users_group_id))
554 "get_users_group_%s" % users_group_id))
555 return users_group.get(users_group_id)
555 return users_group.get(users_group_id)
556
556
557 def get_api_data(self):
557 def get_api_data(self):
558 users_group = self
558 users_group = self
559
559
560 data = dict(
560 data = dict(
561 id=users_group.users_group_id,
561 users_group_id=users_group.users_group_id,
562 group_name=users_group.users_group_name,
562 group_name=users_group.users_group_name,
563 active=users_group.users_group_active,
563 active=users_group.users_group_active,
564 )
564 )
565
565
566 return data
566 return data
567
567
568
568
569 class UsersGroupMember(Base, BaseModel):
569 class UsersGroupMember(Base, BaseModel):
570 __tablename__ = 'users_groups_members'
570 __tablename__ = 'users_groups_members'
571 __table_args__ = (
571 __table_args__ = (
572 {'extend_existing': True, 'mysql_engine': 'InnoDB',
572 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 'mysql_charset': 'utf8'},
573 'mysql_charset': 'utf8'},
574 )
574 )
575
575
576 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
576 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
577 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
578 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
578 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
579
579
580 user = relationship('User', lazy='joined')
580 user = relationship('User', lazy='joined')
581 users_group = relationship('UsersGroup')
581 users_group = relationship('UsersGroup')
582
582
583 def __init__(self, gr_id='', u_id=''):
583 def __init__(self, gr_id='', u_id=''):
584 self.users_group_id = gr_id
584 self.users_group_id = gr_id
585 self.user_id = u_id
585 self.user_id = u_id
586
586
587
587
588 class Repository(Base, BaseModel):
588 class Repository(Base, BaseModel):
589 __tablename__ = 'repositories'
589 __tablename__ = 'repositories'
590 __table_args__ = (
590 __table_args__ = (
591 UniqueConstraint('repo_name'),
591 UniqueConstraint('repo_name'),
592 {'extend_existing': True, 'mysql_engine': 'InnoDB',
592 {'extend_existing': True, 'mysql_engine': 'InnoDB',
593 'mysql_charset': 'utf8'},
593 'mysql_charset': 'utf8'},
594 )
594 )
595
595
596 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
596 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
597 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
597 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
598 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
598 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
599 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
599 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
600 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
600 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
601 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
601 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
602 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
602 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
603 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
603 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
604 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
604 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
605 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
605 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
606 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
606 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
607
607
608 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
608 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
609 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
609 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
610
610
611 user = relationship('User')
611 user = relationship('User')
612 fork = relationship('Repository', remote_side=repo_id)
612 fork = relationship('Repository', remote_side=repo_id)
613 group = relationship('RepoGroup')
613 group = relationship('RepoGroup')
614 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
614 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
615 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
615 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
616 stats = relationship('Statistics', cascade='all', uselist=False)
616 stats = relationship('Statistics', cascade='all', uselist=False)
617
617
618 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
618 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
619
619
620 logs = relationship('UserLog')
620 logs = relationship('UserLog')
621 comments = relationship('ChangesetComment')
621 comments = relationship('ChangesetComment')
622
622
623 def __unicode__(self):
623 def __unicode__(self):
624 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
624 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
625 self.repo_name)
625 self.repo_name)
626
626
627 @classmethod
627 @classmethod
628 def url_sep(cls):
628 def url_sep(cls):
629 return URL_SEP
629 return URL_SEP
630
630
631 @classmethod
631 @classmethod
632 def get_by_repo_name(cls, repo_name):
632 def get_by_repo_name(cls, repo_name):
633 q = Session().query(cls).filter(cls.repo_name == repo_name)
633 q = Session().query(cls).filter(cls.repo_name == repo_name)
634 q = q.options(joinedload(Repository.fork))\
634 q = q.options(joinedload(Repository.fork))\
635 .options(joinedload(Repository.user))\
635 .options(joinedload(Repository.user))\
636 .options(joinedload(Repository.group))
636 .options(joinedload(Repository.group))
637 return q.scalar()
637 return q.scalar()
638
638
639 @classmethod
639 @classmethod
640 def get_by_full_path(cls, repo_full_path):
640 def get_by_full_path(cls, repo_full_path):
641 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
641 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
642 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
642 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
643
643
644 @classmethod
644 @classmethod
645 def get_repo_forks(cls, repo_id):
645 def get_repo_forks(cls, repo_id):
646 return cls.query().filter(Repository.fork_id == repo_id)
646 return cls.query().filter(Repository.fork_id == repo_id)
647
647
648 @classmethod
648 @classmethod
649 def base_path(cls):
649 def base_path(cls):
650 """
650 """
651 Returns base path when all repos are stored
651 Returns base path when all repos are stored
652
652
653 :param cls:
653 :param cls:
654 """
654 """
655 q = Session().query(RhodeCodeUi)\
655 q = Session().query(RhodeCodeUi)\
656 .filter(RhodeCodeUi.ui_key == cls.url_sep())
656 .filter(RhodeCodeUi.ui_key == cls.url_sep())
657 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
657 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
658 return q.one().ui_value
658 return q.one().ui_value
659
659
660 @property
660 @property
661 def forks(self):
661 def forks(self):
662 """
662 """
663 Return forks of this repo
663 Return forks of this repo
664 """
664 """
665 return Repository.get_repo_forks(self.repo_id)
665 return Repository.get_repo_forks(self.repo_id)
666
666
667 @property
667 @property
668 def parent(self):
668 def parent(self):
669 """
669 """
670 Returns fork parent
670 Returns fork parent
671 """
671 """
672 return self.fork
672 return self.fork
673
673
674 @property
674 @property
675 def just_name(self):
675 def just_name(self):
676 return self.repo_name.split(Repository.url_sep())[-1]
676 return self.repo_name.split(Repository.url_sep())[-1]
677
677
678 @property
678 @property
679 def groups_with_parents(self):
679 def groups_with_parents(self):
680 groups = []
680 groups = []
681 if self.group is None:
681 if self.group is None:
682 return groups
682 return groups
683
683
684 cur_gr = self.group
684 cur_gr = self.group
685 groups.insert(0, cur_gr)
685 groups.insert(0, cur_gr)
686 while 1:
686 while 1:
687 gr = getattr(cur_gr, 'parent_group', None)
687 gr = getattr(cur_gr, 'parent_group', None)
688 cur_gr = cur_gr.parent_group
688 cur_gr = cur_gr.parent_group
689 if gr is None:
689 if gr is None:
690 break
690 break
691 groups.insert(0, gr)
691 groups.insert(0, gr)
692
692
693 return groups
693 return groups
694
694
695 @property
695 @property
696 def groups_and_repo(self):
696 def groups_and_repo(self):
697 return self.groups_with_parents, self.just_name
697 return self.groups_with_parents, self.just_name
698
698
699 @LazyProperty
699 @LazyProperty
700 def repo_path(self):
700 def repo_path(self):
701 """
701 """
702 Returns base full path for that repository means where it actually
702 Returns base full path for that repository means where it actually
703 exists on a filesystem
703 exists on a filesystem
704 """
704 """
705 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
705 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
706 Repository.url_sep())
706 Repository.url_sep())
707 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
707 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
708 return q.one().ui_value
708 return q.one().ui_value
709
709
710 @property
710 @property
711 def repo_full_path(self):
711 def repo_full_path(self):
712 p = [self.repo_path]
712 p = [self.repo_path]
713 # we need to split the name by / since this is how we store the
713 # we need to split the name by / since this is how we store the
714 # names in the database, but that eventually needs to be converted
714 # names in the database, but that eventually needs to be converted
715 # into a valid system path
715 # into a valid system path
716 p += self.repo_name.split(Repository.url_sep())
716 p += self.repo_name.split(Repository.url_sep())
717 return os.path.join(*p)
717 return os.path.join(*p)
718
718
719 def get_new_name(self, repo_name):
719 def get_new_name(self, repo_name):
720 """
720 """
721 returns new full repository name based on assigned group and new new
721 returns new full repository name based on assigned group and new new
722
722
723 :param group_name:
723 :param group_name:
724 """
724 """
725 path_prefix = self.group.full_path_splitted if self.group else []
725 path_prefix = self.group.full_path_splitted if self.group else []
726 return Repository.url_sep().join(path_prefix + [repo_name])
726 return Repository.url_sep().join(path_prefix + [repo_name])
727
727
728 @property
728 @property
729 def _ui(self):
729 def _ui(self):
730 """
730 """
731 Creates an db based ui object for this repository
731 Creates an db based ui object for this repository
732 """
732 """
733 from mercurial import ui
733 from mercurial import ui
734 from mercurial import config
734 from mercurial import config
735 baseui = ui.ui()
735 baseui = ui.ui()
736
736
737 #clean the baseui object
737 #clean the baseui object
738 baseui._ocfg = config.config()
738 baseui._ocfg = config.config()
739 baseui._ucfg = config.config()
739 baseui._ucfg = config.config()
740 baseui._tcfg = config.config()
740 baseui._tcfg = config.config()
741
741
742 ret = RhodeCodeUi.query()\
742 ret = RhodeCodeUi.query()\
743 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
743 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
744
744
745 hg_ui = ret
745 hg_ui = ret
746 for ui_ in hg_ui:
746 for ui_ in hg_ui:
747 if ui_.ui_active:
747 if ui_.ui_active:
748 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
748 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
749 ui_.ui_key, ui_.ui_value)
749 ui_.ui_key, ui_.ui_value)
750 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
750 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
751
751
752 return baseui
752 return baseui
753
753
754 @classmethod
754 @classmethod
755 def inject_ui(cls, repo, extras={}):
755 def inject_ui(cls, repo, extras={}):
756 from rhodecode.lib.vcs.backends.hg import MercurialRepository
756 from rhodecode.lib.vcs.backends.hg import MercurialRepository
757 from rhodecode.lib.vcs.backends.git import GitRepository
757 from rhodecode.lib.vcs.backends.git import GitRepository
758 required = (MercurialRepository, GitRepository)
758 required = (MercurialRepository, GitRepository)
759 if not isinstance(repo, required):
759 if not isinstance(repo, required):
760 raise Exception('repo must be instance of %s' % required)
760 raise Exception('repo must be instance of %s' % required)
761
761
762 # inject ui extra param to log this action via push logger
762 # inject ui extra param to log this action via push logger
763 for k, v in extras.items():
763 for k, v in extras.items():
764 repo._repo.ui.setconfig('rhodecode_extras', k, v)
764 repo._repo.ui.setconfig('rhodecode_extras', k, v)
765
765
766 @classmethod
766 @classmethod
767 def is_valid(cls, repo_name):
767 def is_valid(cls, repo_name):
768 """
768 """
769 returns True if given repo name is a valid filesystem repository
769 returns True if given repo name is a valid filesystem repository
770
770
771 :param cls:
771 :param cls:
772 :param repo_name:
772 :param repo_name:
773 """
773 """
774 from rhodecode.lib.utils import is_valid_repo
774 from rhodecode.lib.utils import is_valid_repo
775
775
776 return is_valid_repo(repo_name, cls.base_path())
776 return is_valid_repo(repo_name, cls.base_path())
777
777
778 def get_api_data(self):
778 def get_api_data(self):
779 """
779 """
780 Common function for generating repo api data
780 Common function for generating repo api data
781
781
782 """
782 """
783 repo = self
783 repo = self
784 data = dict(
784 data = dict(
785 repo_id=repo.repo_id,
785 repo_id=repo.repo_id,
786 repo_name=repo.repo_name,
786 repo_name=repo.repo_name,
787 repo_type=repo.repo_type,
787 repo_type=repo.repo_type,
788 clone_uri=repo.clone_uri,
788 clone_uri=repo.clone_uri,
789 private=repo.private,
789 private=repo.private,
790 created_on=repo.created_on,
790 created_on=repo.created_on,
791 description=repo.description,
791 description=repo.description,
792 landing_rev=repo.landing_rev,
792 landing_rev=repo.landing_rev,
793 owner=repo.user.username,
793 owner=repo.user.username,
794 fork_of=repo.fork.repo_name if repo.fork else None
794 fork_of=repo.fork.repo_name if repo.fork else None
795 )
795 )
796
796
797 return data
797 return data
798
798
799 #==========================================================================
799 #==========================================================================
800 # SCM PROPERTIES
800 # SCM PROPERTIES
801 #==========================================================================
801 #==========================================================================
802
802
803 def get_changeset(self, rev=None):
803 def get_changeset(self, rev=None):
804 return get_changeset_safe(self.scm_instance, rev)
804 return get_changeset_safe(self.scm_instance, rev)
805
805
806 @property
806 @property
807 def tip(self):
807 def tip(self):
808 return self.get_changeset('tip')
808 return self.get_changeset('tip')
809
809
810 @property
810 @property
811 def author(self):
811 def author(self):
812 return self.tip.author
812 return self.tip.author
813
813
814 @property
814 @property
815 def last_change(self):
815 def last_change(self):
816 return self.scm_instance.last_change
816 return self.scm_instance.last_change
817
817
818 def get_comments(self, revisions=None):
818 def get_comments(self, revisions=None):
819 """
819 """
820 Returns comments for this repository grouped by revisions
820 Returns comments for this repository grouped by revisions
821
821
822 :param revisions: filter query by revisions only
822 :param revisions: filter query by revisions only
823 """
823 """
824 cmts = ChangesetComment.query()\
824 cmts = ChangesetComment.query()\
825 .filter(ChangesetComment.repo == self)
825 .filter(ChangesetComment.repo == self)
826 if revisions:
826 if revisions:
827 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
827 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
828 grouped = defaultdict(list)
828 grouped = defaultdict(list)
829 for cmt in cmts.all():
829 for cmt in cmts.all():
830 grouped[cmt.revision].append(cmt)
830 grouped[cmt.revision].append(cmt)
831 return grouped
831 return grouped
832
832
833 def statuses(self, revisions=None):
833 def statuses(self, revisions=None):
834 """
834 """
835 Returns statuses for this repository
835 Returns statuses for this repository
836
836
837 :param revisions: list of revisions to get statuses for
837 :param revisions: list of revisions to get statuses for
838 :type revisions: list
838 :type revisions: list
839 """
839 """
840
840
841 statuses = ChangesetStatus.query()\
841 statuses = ChangesetStatus.query()\
842 .filter(ChangesetStatus.repo == self)\
842 .filter(ChangesetStatus.repo == self)\
843 .filter(ChangesetStatus.version == 0)
843 .filter(ChangesetStatus.version == 0)
844 if revisions:
844 if revisions:
845 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
845 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
846 grouped = {}
846 grouped = {}
847 for stat in statuses.all():
847 for stat in statuses.all():
848 pr_id = pr_repo = None
848 pr_id = pr_repo = None
849 if stat.pull_request:
849 if stat.pull_request:
850 pr_id = stat.pull_request.pull_request_id
850 pr_id = stat.pull_request.pull_request_id
851 pr_repo = stat.pull_request.other_repo.repo_name
851 pr_repo = stat.pull_request.other_repo.repo_name
852 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
852 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
853 pr_id, pr_repo]
853 pr_id, pr_repo]
854 return grouped
854 return grouped
855
855
856 #==========================================================================
856 #==========================================================================
857 # SCM CACHE INSTANCE
857 # SCM CACHE INSTANCE
858 #==========================================================================
858 #==========================================================================
859
859
860 @property
860 @property
861 def invalidate(self):
861 def invalidate(self):
862 return CacheInvalidation.invalidate(self.repo_name)
862 return CacheInvalidation.invalidate(self.repo_name)
863
863
864 def set_invalidate(self):
864 def set_invalidate(self):
865 """
865 """
866 set a cache for invalidation for this instance
866 set a cache for invalidation for this instance
867 """
867 """
868 CacheInvalidation.set_invalidate(self.repo_name)
868 CacheInvalidation.set_invalidate(self.repo_name)
869
869
870 @LazyProperty
870 @LazyProperty
871 def scm_instance(self):
871 def scm_instance(self):
872 return self.__get_instance()
872 return self.__get_instance()
873
873
874 def scm_instance_cached(self, cache_map=None):
874 def scm_instance_cached(self, cache_map=None):
875 @cache_region('long_term')
875 @cache_region('long_term')
876 def _c(repo_name):
876 def _c(repo_name):
877 return self.__get_instance()
877 return self.__get_instance()
878 rn = self.repo_name
878 rn = self.repo_name
879 log.debug('Getting cached instance of repo')
879 log.debug('Getting cached instance of repo')
880
880
881 if cache_map:
881 if cache_map:
882 # get using prefilled cache_map
882 # get using prefilled cache_map
883 invalidate_repo = cache_map[self.repo_name]
883 invalidate_repo = cache_map[self.repo_name]
884 if invalidate_repo:
884 if invalidate_repo:
885 invalidate_repo = (None if invalidate_repo.cache_active
885 invalidate_repo = (None if invalidate_repo.cache_active
886 else invalidate_repo)
886 else invalidate_repo)
887 else:
887 else:
888 # get from invalidate
888 # get from invalidate
889 invalidate_repo = self.invalidate
889 invalidate_repo = self.invalidate
890
890
891 if invalidate_repo is not None:
891 if invalidate_repo is not None:
892 region_invalidate(_c, None, rn)
892 region_invalidate(_c, None, rn)
893 # update our cache
893 # update our cache
894 CacheInvalidation.set_valid(invalidate_repo.cache_key)
894 CacheInvalidation.set_valid(invalidate_repo.cache_key)
895 return _c(rn)
895 return _c(rn)
896
896
897 def __get_instance(self):
897 def __get_instance(self):
898 repo_full_path = self.repo_full_path
898 repo_full_path = self.repo_full_path
899 try:
899 try:
900 alias = get_scm(repo_full_path)[0]
900 alias = get_scm(repo_full_path)[0]
901 log.debug('Creating instance of %s repository' % alias)
901 log.debug('Creating instance of %s repository' % alias)
902 backend = get_backend(alias)
902 backend = get_backend(alias)
903 except VCSError:
903 except VCSError:
904 log.error(traceback.format_exc())
904 log.error(traceback.format_exc())
905 log.error('Perhaps this repository is in db and not in '
905 log.error('Perhaps this repository is in db and not in '
906 'filesystem run rescan repositories with '
906 'filesystem run rescan repositories with '
907 '"destroy old data " option from admin panel')
907 '"destroy old data " option from admin panel')
908 return
908 return
909
909
910 if alias == 'hg':
910 if alias == 'hg':
911
911
912 repo = backend(safe_str(repo_full_path), create=False,
912 repo = backend(safe_str(repo_full_path), create=False,
913 baseui=self._ui)
913 baseui=self._ui)
914 # skip hidden web repository
914 # skip hidden web repository
915 if repo._get_hidden():
915 if repo._get_hidden():
916 return
916 return
917 else:
917 else:
918 repo = backend(repo_full_path, create=False)
918 repo = backend(repo_full_path, create=False)
919
919
920 return repo
920 return repo
921
921
922
922
923 class RepoGroup(Base, BaseModel):
923 class RepoGroup(Base, BaseModel):
924 __tablename__ = 'groups'
924 __tablename__ = 'groups'
925 __table_args__ = (
925 __table_args__ = (
926 UniqueConstraint('group_name', 'group_parent_id'),
926 UniqueConstraint('group_name', 'group_parent_id'),
927 CheckConstraint('group_id != group_parent_id'),
927 CheckConstraint('group_id != group_parent_id'),
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
929 'mysql_charset': 'utf8'},
929 'mysql_charset': 'utf8'},
930 )
930 )
931 __mapper_args__ = {'order_by': 'group_name'}
931 __mapper_args__ = {'order_by': 'group_name'}
932
932
933 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
933 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
934 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
934 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
935 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
935 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
936 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
936 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
937
937
938 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
938 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
939 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
939 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
940
940
941 parent_group = relationship('RepoGroup', remote_side=group_id)
941 parent_group = relationship('RepoGroup', remote_side=group_id)
942
942
943 def __init__(self, group_name='', parent_group=None):
943 def __init__(self, group_name='', parent_group=None):
944 self.group_name = group_name
944 self.group_name = group_name
945 self.parent_group = parent_group
945 self.parent_group = parent_group
946
946
947 def __unicode__(self):
947 def __unicode__(self):
948 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
948 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
949 self.group_name)
949 self.group_name)
950
950
951 @classmethod
951 @classmethod
952 def groups_choices(cls):
952 def groups_choices(cls):
953 from webhelpers.html import literal as _literal
953 from webhelpers.html import literal as _literal
954 repo_groups = [('', '')]
954 repo_groups = [('', '')]
955 sep = ' &raquo; '
955 sep = ' &raquo; '
956 _name = lambda k: _literal(sep.join(k))
956 _name = lambda k: _literal(sep.join(k))
957
957
958 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
958 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
959 for x in cls.query().all()])
959 for x in cls.query().all()])
960
960
961 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
961 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
962 return repo_groups
962 return repo_groups
963
963
964 @classmethod
964 @classmethod
965 def url_sep(cls):
965 def url_sep(cls):
966 return URL_SEP
966 return URL_SEP
967
967
968 @classmethod
968 @classmethod
969 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
969 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
970 if case_insensitive:
970 if case_insensitive:
971 gr = cls.query()\
971 gr = cls.query()\
972 .filter(cls.group_name.ilike(group_name))
972 .filter(cls.group_name.ilike(group_name))
973 else:
973 else:
974 gr = cls.query()\
974 gr = cls.query()\
975 .filter(cls.group_name == group_name)
975 .filter(cls.group_name == group_name)
976 if cache:
976 if cache:
977 gr = gr.options(FromCache(
977 gr = gr.options(FromCache(
978 "sql_cache_short",
978 "sql_cache_short",
979 "get_group_%s" % _hash_key(group_name)
979 "get_group_%s" % _hash_key(group_name)
980 )
980 )
981 )
981 )
982 return gr.scalar()
982 return gr.scalar()
983
983
984 @property
984 @property
985 def parents(self):
985 def parents(self):
986 parents_recursion_limit = 5
986 parents_recursion_limit = 5
987 groups = []
987 groups = []
988 if self.parent_group is None:
988 if self.parent_group is None:
989 return groups
989 return groups
990 cur_gr = self.parent_group
990 cur_gr = self.parent_group
991 groups.insert(0, cur_gr)
991 groups.insert(0, cur_gr)
992 cnt = 0
992 cnt = 0
993 while 1:
993 while 1:
994 cnt += 1
994 cnt += 1
995 gr = getattr(cur_gr, 'parent_group', None)
995 gr = getattr(cur_gr, 'parent_group', None)
996 cur_gr = cur_gr.parent_group
996 cur_gr = cur_gr.parent_group
997 if gr is None:
997 if gr is None:
998 break
998 break
999 if cnt == parents_recursion_limit:
999 if cnt == parents_recursion_limit:
1000 # this will prevent accidental infinit loops
1000 # this will prevent accidental infinit loops
1001 log.error('group nested more than %s' %
1001 log.error('group nested more than %s' %
1002 parents_recursion_limit)
1002 parents_recursion_limit)
1003 break
1003 break
1004
1004
1005 groups.insert(0, gr)
1005 groups.insert(0, gr)
1006 return groups
1006 return groups
1007
1007
1008 @property
1008 @property
1009 def children(self):
1009 def children(self):
1010 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1010 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1011
1011
1012 @property
1012 @property
1013 def name(self):
1013 def name(self):
1014 return self.group_name.split(RepoGroup.url_sep())[-1]
1014 return self.group_name.split(RepoGroup.url_sep())[-1]
1015
1015
1016 @property
1016 @property
1017 def full_path(self):
1017 def full_path(self):
1018 return self.group_name
1018 return self.group_name
1019
1019
1020 @property
1020 @property
1021 def full_path_splitted(self):
1021 def full_path_splitted(self):
1022 return self.group_name.split(RepoGroup.url_sep())
1022 return self.group_name.split(RepoGroup.url_sep())
1023
1023
1024 @property
1024 @property
1025 def repositories(self):
1025 def repositories(self):
1026 return Repository.query()\
1026 return Repository.query()\
1027 .filter(Repository.group == self)\
1027 .filter(Repository.group == self)\
1028 .order_by(Repository.repo_name)
1028 .order_by(Repository.repo_name)
1029
1029
1030 @property
1030 @property
1031 def repositories_recursive_count(self):
1031 def repositories_recursive_count(self):
1032 cnt = self.repositories.count()
1032 cnt = self.repositories.count()
1033
1033
1034 def children_count(group):
1034 def children_count(group):
1035 cnt = 0
1035 cnt = 0
1036 for child in group.children:
1036 for child in group.children:
1037 cnt += child.repositories.count()
1037 cnt += child.repositories.count()
1038 cnt += children_count(child)
1038 cnt += children_count(child)
1039 return cnt
1039 return cnt
1040
1040
1041 return cnt + children_count(self)
1041 return cnt + children_count(self)
1042
1042
1043 def get_new_name(self, group_name):
1043 def get_new_name(self, group_name):
1044 """
1044 """
1045 returns new full group name based on parent and new name
1045 returns new full group name based on parent and new name
1046
1046
1047 :param group_name:
1047 :param group_name:
1048 """
1048 """
1049 path_prefix = (self.parent_group.full_path_splitted if
1049 path_prefix = (self.parent_group.full_path_splitted if
1050 self.parent_group else [])
1050 self.parent_group else [])
1051 return RepoGroup.url_sep().join(path_prefix + [group_name])
1051 return RepoGroup.url_sep().join(path_prefix + [group_name])
1052
1052
1053
1053
1054 class Permission(Base, BaseModel):
1054 class Permission(Base, BaseModel):
1055 __tablename__ = 'permissions'
1055 __tablename__ = 'permissions'
1056 __table_args__ = (
1056 __table_args__ = (
1057 Index('p_perm_name_idx', 'permission_name'),
1057 Index('p_perm_name_idx', 'permission_name'),
1058 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1058 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1059 'mysql_charset': 'utf8'},
1059 'mysql_charset': 'utf8'},
1060 )
1060 )
1061 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1061 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1062 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1062 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1063 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1063 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1064
1064
1065 def __unicode__(self):
1065 def __unicode__(self):
1066 return u"<%s('%s:%s')>" % (
1066 return u"<%s('%s:%s')>" % (
1067 self.__class__.__name__, self.permission_id, self.permission_name
1067 self.__class__.__name__, self.permission_id, self.permission_name
1068 )
1068 )
1069
1069
1070 @classmethod
1070 @classmethod
1071 def get_by_key(cls, key):
1071 def get_by_key(cls, key):
1072 return cls.query().filter(cls.permission_name == key).scalar()
1072 return cls.query().filter(cls.permission_name == key).scalar()
1073
1073
1074 @classmethod
1074 @classmethod
1075 def get_default_perms(cls, default_user_id):
1075 def get_default_perms(cls, default_user_id):
1076 q = Session().query(UserRepoToPerm, Repository, cls)\
1076 q = Session().query(UserRepoToPerm, Repository, cls)\
1077 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1077 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1078 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1078 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1079 .filter(UserRepoToPerm.user_id == default_user_id)
1079 .filter(UserRepoToPerm.user_id == default_user_id)
1080
1080
1081 return q.all()
1081 return q.all()
1082
1082
1083 @classmethod
1083 @classmethod
1084 def get_default_group_perms(cls, default_user_id):
1084 def get_default_group_perms(cls, default_user_id):
1085 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1085 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1086 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1086 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1087 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1087 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1088 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1088 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1089
1089
1090 return q.all()
1090 return q.all()
1091
1091
1092
1092
1093 class UserRepoToPerm(Base, BaseModel):
1093 class UserRepoToPerm(Base, BaseModel):
1094 __tablename__ = 'repo_to_perm'
1094 __tablename__ = 'repo_to_perm'
1095 __table_args__ = (
1095 __table_args__ = (
1096 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1096 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1097 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1097 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1098 'mysql_charset': 'utf8'}
1098 'mysql_charset': 'utf8'}
1099 )
1099 )
1100 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1100 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1101 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1101 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1102 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1102 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1103 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1103 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1104
1104
1105 user = relationship('User')
1105 user = relationship('User')
1106 repository = relationship('Repository')
1106 repository = relationship('Repository')
1107 permission = relationship('Permission')
1107 permission = relationship('Permission')
1108
1108
1109 @classmethod
1109 @classmethod
1110 def create(cls, user, repository, permission):
1110 def create(cls, user, repository, permission):
1111 n = cls()
1111 n = cls()
1112 n.user = user
1112 n.user = user
1113 n.repository = repository
1113 n.repository = repository
1114 n.permission = permission
1114 n.permission = permission
1115 Session().add(n)
1115 Session().add(n)
1116 return n
1116 return n
1117
1117
1118 def __unicode__(self):
1118 def __unicode__(self):
1119 return u'<user:%s => %s >' % (self.user, self.repository)
1119 return u'<user:%s => %s >' % (self.user, self.repository)
1120
1120
1121
1121
1122 class UserToPerm(Base, BaseModel):
1122 class UserToPerm(Base, BaseModel):
1123 __tablename__ = 'user_to_perm'
1123 __tablename__ = 'user_to_perm'
1124 __table_args__ = (
1124 __table_args__ = (
1125 UniqueConstraint('user_id', 'permission_id'),
1125 UniqueConstraint('user_id', 'permission_id'),
1126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1127 'mysql_charset': 'utf8'}
1127 'mysql_charset': 'utf8'}
1128 )
1128 )
1129 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1129 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1130 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1130 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1131 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1131 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1132
1132
1133 user = relationship('User')
1133 user = relationship('User')
1134 permission = relationship('Permission', lazy='joined')
1134 permission = relationship('Permission', lazy='joined')
1135
1135
1136
1136
1137 class UsersGroupRepoToPerm(Base, BaseModel):
1137 class UsersGroupRepoToPerm(Base, BaseModel):
1138 __tablename__ = 'users_group_repo_to_perm'
1138 __tablename__ = 'users_group_repo_to_perm'
1139 __table_args__ = (
1139 __table_args__ = (
1140 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1140 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1141 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1141 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1142 'mysql_charset': 'utf8'}
1142 'mysql_charset': 'utf8'}
1143 )
1143 )
1144 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1144 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1145 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1145 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1146 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1146 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1147 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1147 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1148
1148
1149 users_group = relationship('UsersGroup')
1149 users_group = relationship('UsersGroup')
1150 permission = relationship('Permission')
1150 permission = relationship('Permission')
1151 repository = relationship('Repository')
1151 repository = relationship('Repository')
1152
1152
1153 @classmethod
1153 @classmethod
1154 def create(cls, users_group, repository, permission):
1154 def create(cls, users_group, repository, permission):
1155 n = cls()
1155 n = cls()
1156 n.users_group = users_group
1156 n.users_group = users_group
1157 n.repository = repository
1157 n.repository = repository
1158 n.permission = permission
1158 n.permission = permission
1159 Session().add(n)
1159 Session().add(n)
1160 return n
1160 return n
1161
1161
1162 def __unicode__(self):
1162 def __unicode__(self):
1163 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1163 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1164
1164
1165
1165
1166 class UsersGroupToPerm(Base, BaseModel):
1166 class UsersGroupToPerm(Base, BaseModel):
1167 __tablename__ = 'users_group_to_perm'
1167 __tablename__ = 'users_group_to_perm'
1168 __table_args__ = (
1168 __table_args__ = (
1169 UniqueConstraint('users_group_id', 'permission_id',),
1169 UniqueConstraint('users_group_id', 'permission_id',),
1170 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1170 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1171 'mysql_charset': 'utf8'}
1171 'mysql_charset': 'utf8'}
1172 )
1172 )
1173 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1173 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1174 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1174 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1175 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1175 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1176
1176
1177 users_group = relationship('UsersGroup')
1177 users_group = relationship('UsersGroup')
1178 permission = relationship('Permission')
1178 permission = relationship('Permission')
1179
1179
1180
1180
1181 class UserRepoGroupToPerm(Base, BaseModel):
1181 class UserRepoGroupToPerm(Base, BaseModel):
1182 __tablename__ = 'user_repo_group_to_perm'
1182 __tablename__ = 'user_repo_group_to_perm'
1183 __table_args__ = (
1183 __table_args__ = (
1184 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1184 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1185 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1185 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1186 'mysql_charset': 'utf8'}
1186 'mysql_charset': 'utf8'}
1187 )
1187 )
1188
1188
1189 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1189 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1190 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1190 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1191 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1191 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1192 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1192 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1193
1193
1194 user = relationship('User')
1194 user = relationship('User')
1195 group = relationship('RepoGroup')
1195 group = relationship('RepoGroup')
1196 permission = relationship('Permission')
1196 permission = relationship('Permission')
1197
1197
1198
1198
1199 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1199 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1200 __tablename__ = 'users_group_repo_group_to_perm'
1200 __tablename__ = 'users_group_repo_group_to_perm'
1201 __table_args__ = (
1201 __table_args__ = (
1202 UniqueConstraint('users_group_id', 'group_id'),
1202 UniqueConstraint('users_group_id', 'group_id'),
1203 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1203 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1204 'mysql_charset': 'utf8'}
1204 'mysql_charset': 'utf8'}
1205 )
1205 )
1206
1206
1207 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1207 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1208 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1208 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1209 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1209 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1210 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1210 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1211
1211
1212 users_group = relationship('UsersGroup')
1212 users_group = relationship('UsersGroup')
1213 permission = relationship('Permission')
1213 permission = relationship('Permission')
1214 group = relationship('RepoGroup')
1214 group = relationship('RepoGroup')
1215
1215
1216
1216
1217 class Statistics(Base, BaseModel):
1217 class Statistics(Base, BaseModel):
1218 __tablename__ = 'statistics'
1218 __tablename__ = 'statistics'
1219 __table_args__ = (
1219 __table_args__ = (
1220 UniqueConstraint('repository_id'),
1220 UniqueConstraint('repository_id'),
1221 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1221 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1222 'mysql_charset': 'utf8'}
1222 'mysql_charset': 'utf8'}
1223 )
1223 )
1224 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1224 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1225 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1225 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1226 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1226 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1227 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1227 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1228 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1228 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1229 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1229 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1230
1230
1231 repository = relationship('Repository', single_parent=True)
1231 repository = relationship('Repository', single_parent=True)
1232
1232
1233
1233
1234 class UserFollowing(Base, BaseModel):
1234 class UserFollowing(Base, BaseModel):
1235 __tablename__ = 'user_followings'
1235 __tablename__ = 'user_followings'
1236 __table_args__ = (
1236 __table_args__ = (
1237 UniqueConstraint('user_id', 'follows_repository_id'),
1237 UniqueConstraint('user_id', 'follows_repository_id'),
1238 UniqueConstraint('user_id', 'follows_user_id'),
1238 UniqueConstraint('user_id', 'follows_user_id'),
1239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1240 'mysql_charset': 'utf8'}
1240 'mysql_charset': 'utf8'}
1241 )
1241 )
1242
1242
1243 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1243 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1244 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1244 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1245 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1245 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1246 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1246 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1247 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1247 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1248
1248
1249 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1249 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1250
1250
1251 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1251 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1252 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1252 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1253
1253
1254 @classmethod
1254 @classmethod
1255 def get_repo_followers(cls, repo_id):
1255 def get_repo_followers(cls, repo_id):
1256 return cls.query().filter(cls.follows_repo_id == repo_id)
1256 return cls.query().filter(cls.follows_repo_id == repo_id)
1257
1257
1258
1258
1259 class CacheInvalidation(Base, BaseModel):
1259 class CacheInvalidation(Base, BaseModel):
1260 __tablename__ = 'cache_invalidation'
1260 __tablename__ = 'cache_invalidation'
1261 __table_args__ = (
1261 __table_args__ = (
1262 UniqueConstraint('cache_key'),
1262 UniqueConstraint('cache_key'),
1263 Index('key_idx', 'cache_key'),
1263 Index('key_idx', 'cache_key'),
1264 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1264 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1265 'mysql_charset': 'utf8'},
1265 'mysql_charset': 'utf8'},
1266 )
1266 )
1267 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1267 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1268 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1268 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1269 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1269 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1270 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1270 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1271
1271
1272 def __init__(self, cache_key, cache_args=''):
1272 def __init__(self, cache_key, cache_args=''):
1273 self.cache_key = cache_key
1273 self.cache_key = cache_key
1274 self.cache_args = cache_args
1274 self.cache_args = cache_args
1275 self.cache_active = False
1275 self.cache_active = False
1276
1276
1277 def __unicode__(self):
1277 def __unicode__(self):
1278 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1278 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1279 self.cache_id, self.cache_key)
1279 self.cache_id, self.cache_key)
1280
1280
1281 @classmethod
1281 @classmethod
1282 def clear_cache(cls):
1282 def clear_cache(cls):
1283 cls.query().delete()
1283 cls.query().delete()
1284
1284
1285 @classmethod
1285 @classmethod
1286 def _get_key(cls, key):
1286 def _get_key(cls, key):
1287 """
1287 """
1288 Wrapper for generating a key, together with a prefix
1288 Wrapper for generating a key, together with a prefix
1289
1289
1290 :param key:
1290 :param key:
1291 """
1291 """
1292 import rhodecode
1292 import rhodecode
1293 prefix = ''
1293 prefix = ''
1294 iid = rhodecode.CONFIG.get('instance_id')
1294 iid = rhodecode.CONFIG.get('instance_id')
1295 if iid:
1295 if iid:
1296 prefix = iid
1296 prefix = iid
1297 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1297 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1298
1298
1299 @classmethod
1299 @classmethod
1300 def get_by_key(cls, key):
1300 def get_by_key(cls, key):
1301 return cls.query().filter(cls.cache_key == key).scalar()
1301 return cls.query().filter(cls.cache_key == key).scalar()
1302
1302
1303 @classmethod
1303 @classmethod
1304 def _get_or_create_key(cls, key, prefix, org_key):
1304 def _get_or_create_key(cls, key, prefix, org_key):
1305 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1305 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1306 if not inv_obj:
1306 if not inv_obj:
1307 try:
1307 try:
1308 inv_obj = CacheInvalidation(key, org_key)
1308 inv_obj = CacheInvalidation(key, org_key)
1309 Session().add(inv_obj)
1309 Session().add(inv_obj)
1310 Session().commit()
1310 Session().commit()
1311 except Exception:
1311 except Exception:
1312 log.error(traceback.format_exc())
1312 log.error(traceback.format_exc())
1313 Session().rollback()
1313 Session().rollback()
1314 return inv_obj
1314 return inv_obj
1315
1315
1316 @classmethod
1316 @classmethod
1317 def invalidate(cls, key):
1317 def invalidate(cls, key):
1318 """
1318 """
1319 Returns Invalidation object if this given key should be invalidated
1319 Returns Invalidation object if this given key should be invalidated
1320 None otherwise. `cache_active = False` means that this cache
1320 None otherwise. `cache_active = False` means that this cache
1321 state is not valid and needs to be invalidated
1321 state is not valid and needs to be invalidated
1322
1322
1323 :param key:
1323 :param key:
1324 """
1324 """
1325
1325
1326 key, _prefix, _org_key = cls._get_key(key)
1326 key, _prefix, _org_key = cls._get_key(key)
1327 inv = cls._get_or_create_key(key, _prefix, _org_key)
1327 inv = cls._get_or_create_key(key, _prefix, _org_key)
1328
1328
1329 if inv and inv.cache_active is False:
1329 if inv and inv.cache_active is False:
1330 return inv
1330 return inv
1331
1331
1332 @classmethod
1332 @classmethod
1333 def set_invalidate(cls, key):
1333 def set_invalidate(cls, key):
1334 """
1334 """
1335 Mark this Cache key for invalidation
1335 Mark this Cache key for invalidation
1336
1336
1337 :param key:
1337 :param key:
1338 """
1338 """
1339
1339
1340 key, _prefix, _org_key = cls._get_key(key)
1340 key, _prefix, _org_key = cls._get_key(key)
1341 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1341 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1342 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1342 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1343 _org_key))
1343 _org_key))
1344 try:
1344 try:
1345 for inv_obj in inv_objs:
1345 for inv_obj in inv_objs:
1346 if inv_obj:
1346 if inv_obj:
1347 inv_obj.cache_active = False
1347 inv_obj.cache_active = False
1348
1348
1349 Session().add(inv_obj)
1349 Session().add(inv_obj)
1350 Session().commit()
1350 Session().commit()
1351 except Exception:
1351 except Exception:
1352 log.error(traceback.format_exc())
1352 log.error(traceback.format_exc())
1353 Session().rollback()
1353 Session().rollback()
1354
1354
1355 @classmethod
1355 @classmethod
1356 def set_valid(cls, key):
1356 def set_valid(cls, key):
1357 """
1357 """
1358 Mark this cache key as active and currently cached
1358 Mark this cache key as active and currently cached
1359
1359
1360 :param key:
1360 :param key:
1361 """
1361 """
1362 inv_obj = cls.get_by_key(key)
1362 inv_obj = cls.get_by_key(key)
1363 inv_obj.cache_active = True
1363 inv_obj.cache_active = True
1364 Session().add(inv_obj)
1364 Session().add(inv_obj)
1365 Session().commit()
1365 Session().commit()
1366
1366
1367 @classmethod
1367 @classmethod
1368 def get_cache_map(cls):
1368 def get_cache_map(cls):
1369
1369
1370 class cachemapdict(dict):
1370 class cachemapdict(dict):
1371
1371
1372 def __init__(self, *args, **kwargs):
1372 def __init__(self, *args, **kwargs):
1373 fixkey = kwargs.get('fixkey')
1373 fixkey = kwargs.get('fixkey')
1374 if fixkey:
1374 if fixkey:
1375 del kwargs['fixkey']
1375 del kwargs['fixkey']
1376 self.fixkey = fixkey
1376 self.fixkey = fixkey
1377 super(cachemapdict, self).__init__(*args, **kwargs)
1377 super(cachemapdict, self).__init__(*args, **kwargs)
1378
1378
1379 def __getattr__(self, name):
1379 def __getattr__(self, name):
1380 key = name
1380 key = name
1381 if self.fixkey:
1381 if self.fixkey:
1382 key, _prefix, _org_key = cls._get_key(key)
1382 key, _prefix, _org_key = cls._get_key(key)
1383 if key in self.__dict__:
1383 if key in self.__dict__:
1384 return self.__dict__[key]
1384 return self.__dict__[key]
1385 else:
1385 else:
1386 return self[key]
1386 return self[key]
1387
1387
1388 def __getitem__(self, key):
1388 def __getitem__(self, key):
1389 if self.fixkey:
1389 if self.fixkey:
1390 key, _prefix, _org_key = cls._get_key(key)
1390 key, _prefix, _org_key = cls._get_key(key)
1391 try:
1391 try:
1392 return super(cachemapdict, self).__getitem__(key)
1392 return super(cachemapdict, self).__getitem__(key)
1393 except KeyError:
1393 except KeyError:
1394 return
1394 return
1395
1395
1396 cache_map = cachemapdict(fixkey=True)
1396 cache_map = cachemapdict(fixkey=True)
1397 for obj in cls.query().all():
1397 for obj in cls.query().all():
1398 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1398 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1399 return cache_map
1399 return cache_map
1400
1400
1401
1401
1402 class ChangesetComment(Base, BaseModel):
1402 class ChangesetComment(Base, BaseModel):
1403 __tablename__ = 'changeset_comments'
1403 __tablename__ = 'changeset_comments'
1404 __table_args__ = (
1404 __table_args__ = (
1405 Index('cc_revision_idx', 'revision'),
1405 Index('cc_revision_idx', 'revision'),
1406 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1406 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1407 'mysql_charset': 'utf8'},
1407 'mysql_charset': 'utf8'},
1408 )
1408 )
1409 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1409 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1410 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1410 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1411 revision = Column('revision', String(40), nullable=True)
1411 revision = Column('revision', String(40), nullable=True)
1412 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1412 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1413 line_no = Column('line_no', Unicode(10), nullable=True)
1413 line_no = Column('line_no', Unicode(10), nullable=True)
1414 f_path = Column('f_path', Unicode(1000), nullable=True)
1414 f_path = Column('f_path', Unicode(1000), nullable=True)
1415 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1415 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1416 text = Column('text', Unicode(25000), nullable=False)
1416 text = Column('text', Unicode(25000), nullable=False)
1417 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1417 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1418
1418
1419 author = relationship('User', lazy='joined')
1419 author = relationship('User', lazy='joined')
1420 repo = relationship('Repository')
1420 repo = relationship('Repository')
1421 status_change = relationship('ChangesetStatus', uselist=False)
1421 status_change = relationship('ChangesetStatus', uselist=False)
1422 pull_request = relationship('PullRequest', lazy='joined')
1422 pull_request = relationship('PullRequest', lazy='joined')
1423
1423
1424 @classmethod
1424 @classmethod
1425 def get_users(cls, revision=None, pull_request_id=None):
1425 def get_users(cls, revision=None, pull_request_id=None):
1426 """
1426 """
1427 Returns user associated with this ChangesetComment. ie those
1427 Returns user associated with this ChangesetComment. ie those
1428 who actually commented
1428 who actually commented
1429
1429
1430 :param cls:
1430 :param cls:
1431 :param revision:
1431 :param revision:
1432 """
1432 """
1433 q = Session().query(User)\
1433 q = Session().query(User)\
1434 .join(ChangesetComment.author)
1434 .join(ChangesetComment.author)
1435 if revision:
1435 if revision:
1436 q = q.filter(cls.revision == revision)
1436 q = q.filter(cls.revision == revision)
1437 elif pull_request_id:
1437 elif pull_request_id:
1438 q = q.filter(cls.pull_request_id == pull_request_id)
1438 q = q.filter(cls.pull_request_id == pull_request_id)
1439 return q.all()
1439 return q.all()
1440
1440
1441
1441
1442 class ChangesetStatus(Base, BaseModel):
1442 class ChangesetStatus(Base, BaseModel):
1443 __tablename__ = 'changeset_statuses'
1443 __tablename__ = 'changeset_statuses'
1444 __table_args__ = (
1444 __table_args__ = (
1445 Index('cs_revision_idx', 'revision'),
1445 Index('cs_revision_idx', 'revision'),
1446 Index('cs_version_idx', 'version'),
1446 Index('cs_version_idx', 'version'),
1447 UniqueConstraint('repo_id', 'revision', 'version'),
1447 UniqueConstraint('repo_id', 'revision', 'version'),
1448 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1448 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1449 'mysql_charset': 'utf8'}
1449 'mysql_charset': 'utf8'}
1450 )
1450 )
1451 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1451 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1452 STATUS_APPROVED = 'approved'
1452 STATUS_APPROVED = 'approved'
1453 STATUS_REJECTED = 'rejected'
1453 STATUS_REJECTED = 'rejected'
1454 STATUS_UNDER_REVIEW = 'under_review'
1454 STATUS_UNDER_REVIEW = 'under_review'
1455
1455
1456 STATUSES = [
1456 STATUSES = [
1457 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1457 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1458 (STATUS_APPROVED, _("Approved")),
1458 (STATUS_APPROVED, _("Approved")),
1459 (STATUS_REJECTED, _("Rejected")),
1459 (STATUS_REJECTED, _("Rejected")),
1460 (STATUS_UNDER_REVIEW, _("Under Review")),
1460 (STATUS_UNDER_REVIEW, _("Under Review")),
1461 ]
1461 ]
1462
1462
1463 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1463 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1464 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1464 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1465 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1466 revision = Column('revision', String(40), nullable=False)
1466 revision = Column('revision', String(40), nullable=False)
1467 status = Column('status', String(128), nullable=False, default=DEFAULT)
1467 status = Column('status', String(128), nullable=False, default=DEFAULT)
1468 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1468 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1469 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1469 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1470 version = Column('version', Integer(), nullable=False, default=0)
1470 version = Column('version', Integer(), nullable=False, default=0)
1471 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1471 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1472
1472
1473 author = relationship('User', lazy='joined')
1473 author = relationship('User', lazy='joined')
1474 repo = relationship('Repository')
1474 repo = relationship('Repository')
1475 comment = relationship('ChangesetComment', lazy='joined')
1475 comment = relationship('ChangesetComment', lazy='joined')
1476 pull_request = relationship('PullRequest', lazy='joined')
1476 pull_request = relationship('PullRequest', lazy='joined')
1477
1477
1478 def __unicode__(self):
1478 def __unicode__(self):
1479 return u"<%s('%s:%s')>" % (
1479 return u"<%s('%s:%s')>" % (
1480 self.__class__.__name__,
1480 self.__class__.__name__,
1481 self.status, self.author
1481 self.status, self.author
1482 )
1482 )
1483
1483
1484 @classmethod
1484 @classmethod
1485 def get_status_lbl(cls, value):
1485 def get_status_lbl(cls, value):
1486 return dict(cls.STATUSES).get(value)
1486 return dict(cls.STATUSES).get(value)
1487
1487
1488 @property
1488 @property
1489 def status_lbl(self):
1489 def status_lbl(self):
1490 return ChangesetStatus.get_status_lbl(self.status)
1490 return ChangesetStatus.get_status_lbl(self.status)
1491
1491
1492
1492
1493 class PullRequest(Base, BaseModel):
1493 class PullRequest(Base, BaseModel):
1494 __tablename__ = 'pull_requests'
1494 __tablename__ = 'pull_requests'
1495 __table_args__ = (
1495 __table_args__ = (
1496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1497 'mysql_charset': 'utf8'},
1497 'mysql_charset': 'utf8'},
1498 )
1498 )
1499
1499
1500 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1500 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1501 title = Column('title', Unicode(256), nullable=True)
1501 title = Column('title', Unicode(256), nullable=True)
1502 description = Column('description', Unicode(10240), nullable=True)
1502 description = Column('description', Unicode(10240), nullable=True)
1503 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1503 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1505 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1505 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1506 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1506 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1507 org_ref = Column('org_ref', Unicode(256), nullable=False)
1507 org_ref = Column('org_ref', Unicode(256), nullable=False)
1508 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1508 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1509 other_ref = Column('other_ref', Unicode(256), nullable=False)
1509 other_ref = Column('other_ref', Unicode(256), nullable=False)
1510
1510
1511 @hybrid_property
1511 @hybrid_property
1512 def revisions(self):
1512 def revisions(self):
1513 return self._revisions.split(':')
1513 return self._revisions.split(':')
1514
1514
1515 @revisions.setter
1515 @revisions.setter
1516 def revisions(self, val):
1516 def revisions(self, val):
1517 self._revisions = ':'.join(val)
1517 self._revisions = ':'.join(val)
1518
1518
1519 author = relationship('User', lazy='joined')
1519 author = relationship('User', lazy='joined')
1520 reviewers = relationship('PullRequestReviewers')
1520 reviewers = relationship('PullRequestReviewers')
1521 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1521 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1522 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1522 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1523
1523
1524 def __json__(self):
1524 def __json__(self):
1525 return dict(
1525 return dict(
1526 revisions=self.revisions
1526 revisions=self.revisions
1527 )
1527 )
1528
1528
1529
1529
1530 class PullRequestReviewers(Base, BaseModel):
1530 class PullRequestReviewers(Base, BaseModel):
1531 __tablename__ = 'pull_request_reviewers'
1531 __tablename__ = 'pull_request_reviewers'
1532 __table_args__ = (
1532 __table_args__ = (
1533 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1533 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1534 'mysql_charset': 'utf8'},
1534 'mysql_charset': 'utf8'},
1535 )
1535 )
1536
1536
1537 def __init__(self, user=None, pull_request=None):
1537 def __init__(self, user=None, pull_request=None):
1538 self.user = user
1538 self.user = user
1539 self.pull_request = pull_request
1539 self.pull_request = pull_request
1540
1540
1541 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1541 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1542 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1542 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1543 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1543 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1544
1544
1545 user = relationship('User')
1545 user = relationship('User')
1546 pull_request = relationship('PullRequest')
1546 pull_request = relationship('PullRequest')
1547
1547
1548
1548
1549 class Notification(Base, BaseModel):
1549 class Notification(Base, BaseModel):
1550 __tablename__ = 'notifications'
1550 __tablename__ = 'notifications'
1551 __table_args__ = (
1551 __table_args__ = (
1552 Index('notification_type_idx', 'type'),
1552 Index('notification_type_idx', 'type'),
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1554 'mysql_charset': 'utf8'},
1554 'mysql_charset': 'utf8'},
1555 )
1555 )
1556
1556
1557 TYPE_CHANGESET_COMMENT = u'cs_comment'
1557 TYPE_CHANGESET_COMMENT = u'cs_comment'
1558 TYPE_MESSAGE = u'message'
1558 TYPE_MESSAGE = u'message'
1559 TYPE_MENTION = u'mention'
1559 TYPE_MENTION = u'mention'
1560 TYPE_REGISTRATION = u'registration'
1560 TYPE_REGISTRATION = u'registration'
1561 TYPE_PULL_REQUEST = u'pull_request'
1561 TYPE_PULL_REQUEST = u'pull_request'
1562 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1562 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1563
1563
1564 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1564 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1565 subject = Column('subject', Unicode(512), nullable=True)
1565 subject = Column('subject', Unicode(512), nullable=True)
1566 body = Column('body', Unicode(50000), nullable=True)
1566 body = Column('body', Unicode(50000), nullable=True)
1567 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1567 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1568 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1568 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1569 type_ = Column('type', Unicode(256))
1569 type_ = Column('type', Unicode(256))
1570
1570
1571 created_by_user = relationship('User')
1571 created_by_user = relationship('User')
1572 notifications_to_users = relationship('UserNotification', lazy='joined',
1572 notifications_to_users = relationship('UserNotification', lazy='joined',
1573 cascade="all, delete, delete-orphan")
1573 cascade="all, delete, delete-orphan")
1574
1574
1575 @property
1575 @property
1576 def recipients(self):
1576 def recipients(self):
1577 return [x.user for x in UserNotification.query()\
1577 return [x.user for x in UserNotification.query()\
1578 .filter(UserNotification.notification == self)\
1578 .filter(UserNotification.notification == self)\
1579 .order_by(UserNotification.user).all()]
1579 .order_by(UserNotification.user).all()]
1580
1580
1581 @classmethod
1581 @classmethod
1582 def create(cls, created_by, subject, body, recipients, type_=None):
1582 def create(cls, created_by, subject, body, recipients, type_=None):
1583 if type_ is None:
1583 if type_ is None:
1584 type_ = Notification.TYPE_MESSAGE
1584 type_ = Notification.TYPE_MESSAGE
1585
1585
1586 notification = cls()
1586 notification = cls()
1587 notification.created_by_user = created_by
1587 notification.created_by_user = created_by
1588 notification.subject = subject
1588 notification.subject = subject
1589 notification.body = body
1589 notification.body = body
1590 notification.type_ = type_
1590 notification.type_ = type_
1591 notification.created_on = datetime.datetime.now()
1591 notification.created_on = datetime.datetime.now()
1592
1592
1593 for u in recipients:
1593 for u in recipients:
1594 assoc = UserNotification()
1594 assoc = UserNotification()
1595 assoc.notification = notification
1595 assoc.notification = notification
1596 u.notifications.append(assoc)
1596 u.notifications.append(assoc)
1597 Session().add(notification)
1597 Session().add(notification)
1598 return notification
1598 return notification
1599
1599
1600 @property
1600 @property
1601 def description(self):
1601 def description(self):
1602 from rhodecode.model.notification import NotificationModel
1602 from rhodecode.model.notification import NotificationModel
1603 return NotificationModel().make_description(self)
1603 return NotificationModel().make_description(self)
1604
1604
1605
1605
1606 class UserNotification(Base, BaseModel):
1606 class UserNotification(Base, BaseModel):
1607 __tablename__ = 'user_to_notification'
1607 __tablename__ = 'user_to_notification'
1608 __table_args__ = (
1608 __table_args__ = (
1609 UniqueConstraint('user_id', 'notification_id'),
1609 UniqueConstraint('user_id', 'notification_id'),
1610 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1610 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1611 'mysql_charset': 'utf8'}
1611 'mysql_charset': 'utf8'}
1612 )
1612 )
1613 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1613 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1614 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1614 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1615 read = Column('read', Boolean, default=False)
1615 read = Column('read', Boolean, default=False)
1616 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1616 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1617
1617
1618 user = relationship('User', lazy="joined")
1618 user = relationship('User', lazy="joined")
1619 notification = relationship('Notification', lazy="joined",
1619 notification = relationship('Notification', lazy="joined",
1620 order_by=lambda: Notification.created_on.desc(),)
1620 order_by=lambda: Notification.created_on.desc(),)
1621
1621
1622 def mark_as_read(self):
1622 def mark_as_read(self):
1623 self.read = True
1623 self.read = True
1624 Session().add(self)
1624 Session().add(self)
1625
1625
1626
1626
1627 class DbMigrateVersion(Base, BaseModel):
1627 class DbMigrateVersion(Base, BaseModel):
1628 __tablename__ = 'db_migrate_version'
1628 __tablename__ = 'db_migrate_version'
1629 __table_args__ = (
1629 __table_args__ = (
1630 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1630 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1631 'mysql_charset': 'utf8'},
1631 'mysql_charset': 'utf8'},
1632 )
1632 )
1633 repository_id = Column('repository_id', String(250), primary_key=True)
1633 repository_id = Column('repository_id', String(250), primary_key=True)
1634 repository_path = Column('repository_path', Text)
1634 repository_path = Column('repository_path', Text)
1635 version = Column('version', Integer)
1635 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now